home *** CD-ROM | disk | FTP | other *** search
/ Skunkware 98 / Skunkware 98.iso / src / mail / pine3.96.tar.gz / pine3.96.tar / pine3.96 / pico / composer.c < prev    next >
C/C++ Source or Header  |  1996-07-10  |  80KB  |  3,303 lines

  1. #if    !defined(lint) && !defined(DOS)
  2. static char rcsid[] = "$Id: composer.c,v 4.115 1996/07/11 00:23:20 mikes Exp $";
  3. #endif
  4. /*
  5.  * Program:    Pine composer routines
  6.  *
  7.  *
  8.  * Michael Seibel
  9.  * Networks and Distributed Computing
  10.  * Computing and Communications
  11.  * University of Washington
  12.  * Administration Builiding, AG-44
  13.  * Seattle, Washington, 98195, USA
  14.  * Internet: mikes@cac.washington.edu
  15.  *
  16.  * Please address all bugs and comments to "pine-bugs@cac.washington.edu"
  17.  *
  18.  *
  19.  * Pine and Pico are registered trademarks of the University of Washington.
  20.  * No commercial use of these trademarks may be made without prior written
  21.  * permission of the University of Washington.
  22.  * 
  23.  * Pine, Pico, and Pilot software and its included text are Copyright
  24.  * 1989-1996 by the University of Washington.
  25.  * 
  26.  * The full text of our legal notices is contained in the file called
  27.  * CPYRIGHT, included with this distribution.
  28.  *
  29.  *
  30.  * NOTES:
  31.  *
  32.  *  - composer.c is the composer for the PINE mail system
  33.  *
  34.  *  - tabled 01/19/90
  35.  *
  36.  *  Notes: These routines aren't incorporated yet, because the composer as
  37.  *         a whole still needs development.  These are ideas that should
  38.  *         be implemented in later releases of PINE.  See the notes in 
  39.  *         pico.c concerning what needs to be done ....
  40.  *
  41.  *  - untabled 01/30/90
  42.  *
  43.  *  Notes: Installed header line editing, with wrapping and unwrapping, 
  44.  *         context sensitive help, and other mail header editing features.
  45.  *
  46.  *  - finalish code cleanup 07/15/91
  47.  * 
  48.  *  Notes: Killed/yanked header lines use emacs kill buffer.
  49.  *         Arbitrarily large headers are handled gracefully.
  50.  *         All formatting handled by FormatLines.
  51.  *
  52.  *  - Work done to optimize display painting 06/26/92
  53.  *         Not as clean as it should be, needs more thought 
  54.  *
  55.  */
  56. #include <stdio.h>
  57. #include <ctype.h>
  58. #include "osdep.h"
  59. #include "pico.h"
  60. #include "estruct.h"
  61. #include "edef.h"
  62. #include "efunc.h"
  63.  
  64.  
  65. #ifdef    ANSI
  66.     int InitEntryText(char *, struct headerentry *);
  67.     int HeaderOffset(int);
  68.     int HeaderFocus(int, int);
  69.     int LineEdit(int);
  70.     int header_downline(int, int);
  71.     int header_upline(int);
  72.     int FormatLines(struct hdr_line *, char *, int, int, int);
  73.     char *strqchr(char *, int, int *);
  74.     int PaintBody(int);
  75.     int ComposerHelp(int);
  76.     int NewTop(void);
  77.     void display_delimiter(int);
  78.     int InvertPrompt(int, int);
  79.     int partial_entries(void);
  80.     int physical_line(struct hdr_line *);
  81.     int strend(char *, int);
  82.     int KillHeaderLine(struct hdr_line *, int);
  83.     int SaveHeaderLines(void);
  84.     char *break_point(char *, int, int, int *);
  85.     int hldelete(struct hdr_line *);
  86.     int is_blank(int, int, int);
  87.     int zotentry(struct hdr_line *);
  88.     void zotcomma(char *);
  89.     struct hdr_line *first_hline(int *);
  90.     struct hdr_line *next_hline(int *, struct hdr_line *);
  91.     struct hdr_line *prev_hline(int *, struct hdr_line *);
  92.     struct hdr_line *first_requested_hline(int *);
  93. #else
  94.     int InitEntryText();
  95.     int HeaderOffset();
  96.     int HeaderFocus();
  97.     int LineEdit();
  98.     int header_downline();
  99.     int header_upline();
  100.     int FormatLines();
  101.     char *strqchr();
  102.     int PaintBody();
  103.     int ComposerHelp();
  104.     int NewTop();
  105.     void display_delimiter();
  106.     int InvertPrompt();
  107.     int partial_entries();
  108.     int physical_line();
  109.     int strend();
  110.     int KillHeaderLine();
  111.     int SaveHeaderLines();
  112.     char *break_point();
  113.     int hldelete();
  114.     int is_blank();
  115.     int zotentry();
  116.     void zotcomma();
  117.     struct hdr_line *first_hline();
  118.     struct hdr_line *next_hline();
  119.     struct hdr_line *prev_hline();
  120.     struct hdr_line *first_requested_hline();
  121. #endif
  122.  
  123.  
  124. /*
  125.  * definition header field array, structures defined in pico.h
  126.  */
  127. struct headerentry *headents;
  128.  
  129.  
  130. /*
  131.  * structure that keeps track of the range of header lines that are
  132.  * to be displayed and other fun stuff about the header
  133.  */
  134. struct on_display ods;                /* global on_display struct */
  135.  
  136.  
  137. /*
  138.  * useful macros
  139.  */
  140. #define    HALLOC()    (struct hdr_line *)malloc(sizeof(struct hdr_line))
  141. #define    LINELEN()    (term.t_ncol - headents[ods.cur_e].prlen)
  142. #define    BOTTOM()    (term.t_nrow - term.t_mrow)
  143. #define    FULL_SCR()    (BOTTOM() - 3)
  144. #define    HALF_SCR()    (FULL_SCR()/2)
  145.  
  146. #ifdef    MOUSE
  147. /*
  148.  * Redefine HeaderEditor to install wrapper required for mouse even
  149.  * handling...
  150.  */
  151. #define    HeaderEditor    HeaderEditorWork
  152. #endif /* MOUSE */
  153.  
  154. #if    (defined(DOS) && !defined(_WINDOWS)) || defined(OS2)
  155. #define    HDR_DELIM    "\xCD\xCD\xCD\xCD\xCD Message Text \xCD\xCD\xCD\xCD\xCD"
  156. #else
  157. #define    HDR_DELIM    "----- Message Text -----"
  158. #endif
  159.  
  160. /*
  161.  * useful declarations
  162.  */
  163. static int     last_key;            /* last keystroke  */
  164.  
  165.  
  166. static KEYMENU menu_header[] = {
  167.     {"^G", "Get Help", KS_SCREENHELP},    {"^X", "Send", KS_SEND},
  168.     {"^R", "Rich Hdr", KS_RICHHDR},    {"^Y", "PrvPg/Top", KS_PREVPAGE},
  169.     {"^K", "Cut Line", KS_CURPOSITION},    {"^O", "Postpone", KS_POSTPONE},
  170.     {"^C", "Cancel", KS_CANCEL},    {"^D", "Del Char", KS_NONE},
  171.     {"^J", "Attach", KS_ATTACH},    {"^V", "NxtPg/End", KS_NEXTPAGE},
  172.     {"^U", "UnDel Line", KS_NONE},    {NULL, NULL}
  173. };
  174. #define    SEND_KEY    1
  175. #define    RICH_KEY    2
  176. #define    CUT_KEY        4
  177. #define    PONE_KEY    5
  178. #define    DEL_KEY        7
  179. #define    ATT_KEY        8
  180. #define    UDEL_KEY    10
  181. #define    TO_KEY        11
  182.  
  183.  
  184. /*
  185.  * function key mappings for header editor
  186.  */
  187. static int ckm[12][2] = {
  188.     { F1,  (CTRL|'G')},
  189.     { F2,  (CTRL|'C')},
  190.     { F3,  (CTRL|'X')},
  191.     { F4,  (CTRL|'D')},
  192.     { F5,  (CTRL|'R')},
  193.     { F6,  (CTRL|'J')},
  194.     { F7,  0 },
  195.     { F8,  0 },
  196.     { F9,  (CTRL|'K')},
  197.     { F10, (CTRL|'U')},
  198.     { F11, (CTRL|'O')},
  199.     { F12, (CTRL|'T')}
  200. };
  201.  
  202.  
  203. /*
  204.  * InitMailHeader - initialize header array, and set beginning editor row 
  205.  *                  range.  The header entry structure should look just like 
  206.  *                  what is written on the screen, the vector 
  207.  *                  (entry, line, offset) will describe the current cursor 
  208.  *                  position in the header.
  209.  *
  210.  *       Returns: TRUE if special header handling was requested,
  211.  *            FALSE under standard default behavior.
  212.  */
  213. InitMailHeader(mp)
  214. PICO  *mp;
  215. {
  216.     char           *addrbuf;
  217.     struct headerentry *he;
  218.     int            rv;
  219.  
  220.     if(!mp->headents){
  221.     headents = NULL;
  222.     return(FALSE);
  223.     }
  224.  
  225.     /*
  226.      * initialize some of on_display structure, others below...
  227.      */
  228.     ods.p_off  = 0;
  229.     ods.p_line = COMPOSER_TOP_LINE;
  230.     ods.top_l = ods.cur_l = NULL;
  231.  
  232.     headents = mp->headents;
  233.     /*--- initialize the fields in the headerent structure ----*/
  234.     for(he = headents; he->name != NULL; he++){
  235.     he->hd_text    = NULL;
  236.     he->display_it = he->display_it ? he->display_it : !he->rich_header;
  237.         if(he->is_attach) {
  238.             /*--- A lot of work to do since attachments are special ---*/
  239.             he->maxlen = 0;
  240.         if(mp->attachments != NULL){
  241.         char   buf[NLINE];
  242.                 int    x = 0;
  243.                 PATMT *ap = mp->attachments;
  244.  
  245.                 addrbuf = (char *)malloc((size_t)1024);
  246.                 addrbuf[0] = '\0';
  247.                 buf[0] = '\0';
  248.                 while(ap){
  249.                  if(ap->filename){
  250.                      sprintf(buf, "%d. %s %s%s%s\"%s\"%s",
  251.                              ++x,
  252.                              ap->filename,
  253.                              ap->size ? "(" : "",
  254.                              ap->size ? ap->size : "",
  255.                              ap->size ? ") " : "",
  256.                              ap->description ? ap->description : "", 
  257.                              ap->next ? "," : "");
  258.                      strcat(addrbuf, buf);
  259.                  }
  260.                  ap = ap->next;
  261.                 }
  262.                 InitEntryText(addrbuf, he);
  263.                 free((char *)addrbuf);
  264.             } else {
  265.                 InitEntryText("", he);
  266.             }
  267.             he->realaddr = NULL;
  268.         } else {
  269.             addrbuf = *(he->realaddr);
  270.             InitEntryText(addrbuf, he);
  271.     }
  272.     }
  273.  
  274.     /*
  275.      * finish initialization and then figure out display layout.
  276.      * first, look for any fields the caller requested we start in.
  277.      */
  278.     if(ods.cur_l = first_requested_hline(&ods.cur_e)){
  279.     ods.top_e = 0;                /* init top_e */
  280.     ods.top_l = first_hline(&ods.top_e);
  281.     rv = TRUE;
  282.     }
  283.     else{
  284.     ods.top_l = ods.cur_l = first_hline(&ods.cur_e);
  285.     ods.top_e = ods.cur_e;
  286.     rv = 0;
  287.     }
  288.  
  289.     UpdateHeader();
  290.     return(rv);
  291. }
  292.  
  293.  
  294.  
  295. /*
  296.  * InitEntryText - Add the given header text into the header entry 
  297.  *           line structure.
  298.  */
  299. InitEntryText(address, e)
  300. char    *address;
  301. struct headerentry *e;
  302. {
  303.     struct  hdr_line    *curline;
  304.     register  int    longest;
  305.  
  306.     /*
  307.      * get first chunk of memory, and tie it to structure...
  308.      */
  309.     if((curline = HALLOC()) == NULL){
  310.         emlwrite("Unable to make room for full Header.", NULL);
  311.         return(FALSE);
  312.     }
  313.     longest = term.t_ncol - e->prlen - 1;
  314.     curline->text[0] = '\0';
  315.     curline->next = NULL;
  316.     curline->prev = NULL;
  317.     e->hd_text = curline;        /* tie it into the list */
  318.  
  319.     if(FormatLines(curline, address, longest, e->break_on_comma, 0) == -1)
  320.       return(FALSE);
  321.     else
  322.       return(TRUE);
  323. }
  324.  
  325.  
  326.  
  327. /*
  328.  *  ResizeHeader - Handle resizing display when SIGWINCH received.
  329.  *
  330.  *    notes:
  331.  *        works OK, but needs thorough testing
  332.  *          
  333.  */
  334. ResizeHeader()
  335. {
  336.     register struct headerentry *i;
  337.     register int offset;
  338.  
  339.     if(!headents)
  340.       return(TRUE);
  341.  
  342.     offset = (ComposerEditing) ? HeaderOffset(ods.cur_e) : 0;
  343.  
  344.     for(i=headents; i->name; i++){        /* format each entry */
  345.     if(FormatLines(i->hd_text, "", (term.t_ncol - i->prlen),
  346.                i->break_on_comma, 0) == -1){
  347.         return(-1);
  348.     }
  349.     }
  350.  
  351.     if(ComposerEditing)                /* restart at the top */
  352.       HeaderFocus(ods.cur_e, offset);        /* fix cur_l and p_off */
  353.     else {
  354.       struct hdr_line *l;
  355.       int              e;
  356.  
  357.       for(i = headents; i->name != NULL; i++);    /* Find last line */
  358.       i--;
  359.       e = i - headents;
  360.       l = headents[e].hd_text;
  361.       if(!headents[e].display_it)
  362.         l = prev_hline(&e, l);            /* last displayable line */
  363.  
  364.       if(!l){
  365.       e = i - headents;
  366.       l = headents[e].hd_text;
  367.       }
  368.  
  369.       HeaderFocus(e, -1);        /* put focus on last line */
  370.     }
  371.  
  372.     if(ComposerTopLine != COMPOSER_TOP_LINE)
  373.       UpdateHeader();
  374.  
  375.     PaintBody(0);
  376.  
  377.     if(ComposerEditing)
  378.       movecursor(ods.p_line, ods.p_off+headents[ods.cur_e].prlen);
  379.  
  380.     (*term.t_flush)();
  381.     return(TRUE);
  382. }
  383.  
  384.  
  385.  
  386. /*
  387.  * HeaderOffset - return the character offset into the given header
  388.  */
  389. HeaderOffset(h)
  390. int    h;
  391. {
  392.     register struct hdr_line *l;
  393.     int         i = 0;
  394.  
  395.     l = headents[h].hd_text;
  396.  
  397.     while(l != ods.cur_l){
  398.     i += strlen(l->text);
  399.     l = l->next;
  400.     }
  401.     return(i+ods.p_off);
  402. }
  403.  
  404.  
  405.  
  406. /*
  407.  * HeaderFocus - put the dot at the given offset into the given header
  408.  */
  409. HeaderFocus(h, offset)
  410. int    h, offset;
  411. {
  412.     register struct hdr_line *l;
  413.     register int    i;
  414.     int         last = 0;
  415.  
  416.     if(offset == -1)                /* focus on last line */
  417.       last = 1;
  418.  
  419.     l = headents[h].hd_text;
  420.     while(1){
  421.     if(last && l->next == NULL){
  422.         break;
  423.     }
  424.     else{
  425.         if((i=strlen(l->text)) >= offset)
  426.           break;
  427.         else
  428.           offset -= i;
  429.     }
  430.     if((l = l->next) == NULL)
  431.       return(FALSE);
  432.     }
  433.  
  434.     ods.cur_l = l;
  435.     ods.p_len = strlen(l->text);
  436.     ods.p_off = (last) ? 0 : offset;
  437.  
  438.     return(TRUE);
  439. }
  440.  
  441.  
  442.  
  443. /*
  444.  * HeaderEditor() - edit the mail header field by field, trapping 
  445.  *                  important key sequences, hand the hard work off
  446.  *                  to LineEdit().  
  447.  *    returns:
  448.  *              -2    if we drop out bottom *and* want to page forward
  449.  *        -1    if we drop out the bottom 
  450.  *        FALSE if editing is cancelled
  451.  *        TRUE  if editing is finished
  452.  */
  453. HeaderEditor(f, n)
  454. int f, n;
  455. {
  456.     register  int    i;
  457.     register  int    ch;
  458.     register  int    status;            /* return status of something*/
  459.     register  char    *bufp;
  460.     struct headerentry *h;
  461.     int                 cur_e, count, retval = -1,
  462.                 hdr_only = (gmode & MDHDRONLY) ? 1 : 0;
  463.     char               *errmss;
  464. #ifdef MOUSE
  465.     MOUSEPRESS        mp;
  466. #endif
  467.  
  468.     if(!headents)
  469.       return(TRUE);                /* nothing to edit! */
  470.  
  471.     ComposerEditing = TRUE;
  472.     display_delimiter(0);            /* provide feedback */
  473.  
  474. #ifdef    _WINDOWS
  475.     mswin_setscrollrange (0);
  476. #endif /* _WINDOWS */
  477.  
  478.     /* 
  479.      * Decide where to begin editing.  if f == TRUE begin editing
  480.      * at the bottom.  this case results from the cursor backing
  481.      * into the editor from the bottom.  otherwise, the user explicitly
  482.      * requested editing the header, and we begin at the top.
  483.      * 
  484.      * further, if f == 1, we moved into the header by hitting the up arrow
  485.      * in the message text, else if f == 2, we moved into the header by
  486.      * moving past the left edge of the top line in the message.  so, make 
  487.      * the end of the last line of the last entry the current cursor position
  488.      * lastly, if f == 3, we moved into the header by hitting backpage() on
  489.      * the top line of the message, so scroll a page back.  
  490.      */
  491.     if(f){
  492.     if(f == 2){                /* 2 leaves cursor at end  */
  493.         struct hdr_line *l = ods.cur_l;
  494.         int              e = ods.cur_e;
  495.  
  496.         /*--- make sure on last field ---*/
  497.         while(l = next_hline(&e, l))
  498.           if(headents[ods.cur_e].display_it){
  499.           ods.cur_l = l;
  500.           ods.cur_e = e;
  501.           }
  502.  
  503.         ods.p_off = 1000;            /* and make sure at EOL    */
  504.     }
  505.     else{
  506.         /*
  507.          * note: assumes that ods.cur_e and ods.cur_l haven't changed
  508.          *       since we left...
  509.          */
  510.  
  511.         /* fix postition */
  512.         if(curwp->w_doto < headents[ods.cur_e].prlen)
  513.           ods.p_off = 0;
  514.         else if(curwp->w_doto < ods.p_off + headents[ods.cur_e].prlen)
  515.           ods.p_off = curwp->w_doto - headents[ods.cur_e].prlen;
  516.         else
  517.           ods.p_off = 1000;
  518.  
  519.         /* and scroll back if needed */
  520.         if(f == 3)
  521.           for(i = 0; header_upline(0) && i <= FULL_SCR(); i++)
  522.         ;
  523.     }
  524.  
  525.     ods.p_line = ComposerTopLine - 2;
  526.     }
  527.     /* else just trust what ods contains */
  528.  
  529.     InvertPrompt(ods.cur_e, TRUE);        /* highlight header field */
  530.     sgarbk = 1;
  531.  
  532.     do{
  533.     if(km_popped){
  534.         km_popped--;
  535.         if(km_popped == 0)
  536.           sgarbk = 1;
  537.     }
  538.  
  539.     if(sgarbk){
  540.         if(km_popped){  /* temporarily change to cause menu to paint */
  541.         term.t_mrow = 2;
  542.         curwp->w_ntrows -= 2;
  543.         movecursor(term.t_nrow-2, 0); /* clear status line, too */
  544.         peeol();
  545.         }
  546.         else if(term.t_mrow == 0)
  547.           PaintBody(1);
  548.  
  549.         ShowPrompt();            /* display correct options */
  550.         sgarbk = 0;
  551.         if(km_popped){
  552.         term.t_mrow = 0;
  553.         curwp->w_ntrows += 2;
  554.         }
  555.     }
  556.  
  557.     ch = LineEdit(!(gmode&MDVIEW));        /* work on the current line */
  558.  
  559.     if(km_popped)
  560.       switch(ch){
  561.         case NODATA:
  562.         case (CTRL|'L'):
  563.           km_popped++;
  564.           break;
  565.         
  566.         default:
  567.           movecursor(term.t_nrow-2, 0);
  568.           peeol();
  569.           movecursor(term.t_nrow-1, 0);
  570.           peeol();
  571.           movecursor(term.t_nrow, 0);
  572.           peeol();
  573.           break;
  574.       }
  575.  
  576.         switch (ch){
  577.       case (CTRL|'R') :            /* Toggle header display */
  578.         if(Pmaster->pine_flags & P_ABOOK){
  579.         if(Pmaster->expander){
  580.             packheader();
  581.             (*Pmaster->expander)(headents);
  582.             PaintBody(0);
  583.             break;
  584.         }
  585.         else
  586.           goto bleep;
  587.         }
  588.  
  589.             /*---- Are there any headers to expand above us? ---*/
  590.             for(h = headents; h != &headents[ods.cur_e]; h++)
  591.               if(h->rich_header)
  592.                 break;
  593.             if(h->rich_header)
  594.           InvertPrompt(ods.cur_e, FALSE);    /* Yes, don't leave inverted */
  595.  
  596.         if(partial_entries()){
  597.                 /*--- Just turned off all rich headers --*/
  598.         if(headents[ods.cur_e].rich_header){
  599.                     /*-- current header got turned off too --*/
  600.             if(headents[ods.cur_e].builder)    /* verify text */
  601.               i = call_builder(&headents[ods.cur_e]) > 0;
  602.                     /* Check below */
  603.                     for(cur_e =ods.cur_e; headents[cur_e].name!=NULL; cur_e++)
  604.                       if(!headents[cur_e].rich_header)
  605.                         break;
  606.                     if(headents[cur_e].name == NULL) {
  607.                         /* didn't find one, check above */
  608.                         for(cur_e =ods.cur_e; headents[cur_e].name!=NULL;
  609.                             cur_e--)
  610.                           if(!headents[cur_e].rich_header)
  611.                             break;
  612.  
  613.                     }
  614.             ods.p_off = 0;
  615.             ods.cur_e = cur_e;
  616.             ods.cur_l = headents[ods.cur_e].hd_text;
  617.         }
  618.         }
  619.  
  620.         ods.p_line = 0;            /* force update */
  621.         UpdateHeader();
  622.         PaintHeader(COMPOSER_TOP_LINE, FALSE);
  623.         PaintBody(1);
  624.         break;
  625.  
  626.       case (CTRL|'C') :            /* bag whole thing ?*/
  627.         if(abort_composer(1, 0) == TRUE)
  628.           return(FALSE);
  629.  
  630.         break;
  631.  
  632.       case (CTRL|'X') :            /* Done. Send it. */
  633.         i = 0;
  634. #ifdef    ATTACHMENTS
  635.         if(headents[ods.cur_e].is_attach){
  636.         /* verify the attachments, and pretty things up in case
  637.          * we come back to the composer due to error...
  638.          */
  639.         if((i = SyncAttach()) != 0){
  640.             sleep(2);        /* give time for error to absorb */
  641.             FormatLines(headents[ods.cur_e].hd_text, "",
  642.                 term.t_ncol - headents[ods.cur_e].prlen,
  643.                 headents[ods.cur_e].break_on_comma, 0);
  644.         }
  645.         }
  646.         else
  647. #endif
  648.         if(headents[ods.cur_e].builder)    /* verify text? */
  649.           i = call_builder(&headents[ods.cur_e]);
  650.  
  651.         if(i < 0)            /* don't leave without a valid addr */
  652.           break;
  653.         else if(i > 0){
  654.         ods.cur_l = headents[ods.cur_e].hd_text; /* attach cur_l */
  655.         ods.p_off = 0;
  656.         ods.p_line = 0;            /* force realignment */
  657.         NewTop();
  658.         }
  659.  
  660.         if(wquit(1,0) == TRUE)
  661.           return(TRUE);
  662.  
  663.         if(i > 0){
  664.         /*
  665.          * need to be careful here because pointers might be messed up.
  666.          * also, could do a better job of finding the right place to
  667.          * put the dot back (i.e., the addr/list that was expanded).
  668.          */
  669.         UpdateHeader();
  670.         PaintHeader(COMPOSER_TOP_LINE, FALSE);
  671.         PaintBody(1);
  672.         }
  673.         break;
  674.  
  675.       case (CTRL|'Z') :            /* Suspend compose */
  676.         if(gmode&MDSSPD){            /* is it allowed? */
  677.         bktoshell();
  678.         PaintBody(0);
  679.         }
  680.         else{
  681.         (*term.t_beep)();
  682.         emlwrite("Unknown Command: ^Z", NULL);
  683.         }
  684.         break;
  685.  
  686.       case (CTRL|'O') :            /* Suspend message */
  687.         if(Pmaster->pine_flags & P_ABOOK)
  688.           goto bleep;
  689.  
  690.         i = 0;
  691.         if(headents[ods.cur_e].is_attach){
  692.         if(SyncAttach() < 0){
  693.             if(mlyesno("Problem with attachments. Postpone anyway?",
  694.                    FALSE) != TRUE){
  695.             if(FormatLines(headents[ods.cur_e].hd_text, "",
  696.                        term.t_ncol - headents[ods.cur_e].prlen,
  697.                        headents[ods.cur_e].break_on_comma,
  698.                        0) == -1)
  699.               emlwrite("\007Format lines failed!", NULL);
  700.             UpdateHeader();
  701.             PaintHeader(COMPOSER_TOP_LINE, FALSE);
  702.             PaintBody(1);
  703.             continue;
  704.             }
  705.         }
  706.         }
  707.         else if(headents[ods.cur_e].builder)
  708.           i = call_builder(&headents[ods.cur_e]);
  709.  
  710.         if(i < 0)            /* don't leave without a valid addr */
  711.           break;
  712.  
  713.         suspend_composer(1, 0);
  714.         return(TRUE);
  715.  
  716. #ifdef    ATTACHMENTS
  717.       case (CTRL|'J') :            /* handle attachments */
  718.         if(Pmaster->pine_flags & P_ABOOK)
  719.           goto bleep;
  720.  
  721.         { char fn[NLINE], sz[32], cmt[NLINE];
  722.           int saved_km_popped;
  723.  
  724.           /*
  725.            * Attachment questions mess with km_popped and assume
  726.            * it is zero to start with.  If we don't set it to zero
  727.            * on entry, the message about mime type will be erased
  728.            * by PaintBody.  If we don't reset it when we come back,
  729.            * the bottom three lines may be messed up.
  730.            */
  731.           saved_km_popped = km_popped;
  732.           km_popped = 0;
  733.  
  734.           if(AskAttach(fn, sz, cmt)){
  735.           status = !AppendAttachment(fn, sz, cmt);
  736.           }
  737.  
  738.           km_popped = saved_km_popped;
  739.           sgarbk = 1;            /* clean up prompt */
  740.         }
  741.         break;
  742. #endif
  743.  
  744.       case (CTRL|'I') :            /* tab */
  745.         ods.p_off = 0;            /* fall through... */
  746.  
  747.       case (CTRL|'N') :
  748.       case K_PAD_DOWN :
  749.         header_downline(!hdr_only, hdr_only);
  750.         break;
  751.  
  752.       case (CTRL|'P') :
  753.       case K_PAD_UP :
  754.         header_upline(1);
  755.         break;
  756.  
  757.       case (CTRL|'V') :            /* down a page */
  758.       case K_PAD_NEXTPAGE:
  759.         cur_e = ods.cur_e;
  760.         if(!next_hline(&cur_e, ods.cur_l)){
  761.         header_downline(!hdr_only, hdr_only);
  762.         if(!(gmode & MDHDRONLY))
  763.           retval = -1;            /* tell caller we fell out */
  764.         }
  765.         else{
  766.         int move_down, bot_pline;
  767.         struct hdr_line *new_cur_l, *line, *next_line, *prev_line;
  768.  
  769.         move_down = BOTTOM() - 2 - ods.p_line;
  770.         if(move_down < 0)
  771.           move_down = 0;
  772.  
  773.         /*
  774.          * Count down move_down lines to find the pointer to the line
  775.          * that we want to become the current line.
  776.          */
  777.         new_cur_l = ods.cur_l;
  778.         cur_e = ods.cur_e;
  779.         for(i = 0; i < move_down; i++){
  780.             next_line = next_hline(&cur_e, new_cur_l);
  781.             if(!next_line)
  782.               break;
  783.  
  784.             new_cur_l = next_line;
  785.         }
  786.  
  787.         /*
  788.          * Now call header_downline until we get down to the
  789.          * new current line, so that the builders all get called.
  790.          * New_cur_l will remain valid since we won't be calling
  791.          * a builder for it during this loop.
  792.          */
  793.         while(ods.cur_l != new_cur_l && header_downline(0, 0))
  794.           ;
  795.         
  796.         /*
  797.          * Count back up, if we're at the bottom, to find the new
  798.          * top line.
  799.          */
  800.         cur_e = ods.cur_e;
  801.         if(next_hline(&cur_e, ods.cur_l) == NULL){
  802.             /*
  803.              * Cursor stops at bottom of headers, which is where
  804.              * we are right now.  Put as much of headers on
  805.              * screen as will fit.  Count up to figure
  806.              * out which line is top_l and which p_line cursor is on.
  807.              */
  808.             cur_e = ods.cur_e;
  809.             line = ods.cur_l;
  810.             /* leave delimiter on screen, too */
  811.             bot_pline = BOTTOM() - 1 - ((gmode & MDHDRONLY) ? 0 : 1);
  812.             for(i = COMPOSER_TOP_LINE; i < bot_pline; i++){
  813.             prev_line = prev_hline(&cur_e, line);
  814.             if(!prev_line)
  815.               break;
  816.             
  817.             line = prev_line;
  818.             }
  819.  
  820.             ods.top_l = line;
  821.             ods.top_e = cur_e;
  822.             ods.p_line = i;
  823.               
  824.         }
  825.         else{
  826.             ods.top_l = ods.cur_l;
  827.             ods.top_e = ods.cur_e;
  828.             /*
  829.              * We don't want to scroll down further than the
  830.              * delimiter, so check to see if that is the case.
  831.              * If it is, we move the p_line down the screen
  832.              * until the bottom line is where we want it.
  833.              */
  834.             bot_pline = BOTTOM() - 1 - ((gmode & MDHDRONLY) ? 0 : 1);
  835.             cur_e = ods.cur_e;
  836.             line = ods.cur_l;
  837.             for(i = bot_pline; i > COMPOSER_TOP_LINE; i--){
  838.             next_line = next_hline(&cur_e, line);
  839.             if(!next_line)
  840.               break;
  841.  
  842.             line = next_line;
  843.             }
  844.  
  845.             /*
  846.              * i is the desired value of p_line.
  847.              * If it is greater than COMPOSER_TOP_LINE, then
  848.              * we need to adjust top_l.
  849.              */
  850.             ods.p_line = i;
  851.             line = ods.top_l;
  852.             cur_e = ods.top_e;
  853.             for(; i > COMPOSER_TOP_LINE; i--){
  854.             prev_line = prev_hline(&cur_e, line);
  855.             if(!prev_line)
  856.               break;
  857.             
  858.             line = prev_line;
  859.             }
  860.  
  861.             ods.top_l = line;
  862.             ods.top_e = cur_e;
  863.  
  864.             /*
  865.              * Special case.  If p_line is within one of the bottom,
  866.              * move it to the bottom.
  867.              */
  868.             if(ods.p_line == bot_pline - 1){
  869.             header_downline(0, 0);
  870.             /* but put these back where we want them */
  871.             ods.p_line = bot_pline;
  872.             ods.top_l = line;
  873.             ods.top_e = cur_e;
  874.             }
  875.         }
  876.  
  877.         UpdateHeader();
  878.         PaintHeader(COMPOSER_TOP_LINE, FALSE);
  879.         PaintBody(1);
  880.         }
  881.  
  882.         break;
  883.  
  884.       case (CTRL|'Y') :            /* up a page */
  885.       case K_PAD_PREVPAGE:
  886.         for(i = 0; header_upline(0) && i <= FULL_SCR(); i++)
  887.           if(i < 0)
  888.         break;
  889.  
  890.         break;
  891.  
  892. #ifdef    MOUSE
  893.       case K_MOUSE:
  894.         mouse_get_last (NULL, &mp);
  895.         if (!mp.doubleclick) {
  896.         if (mp.row < ods.p_line) {
  897.             for (i = ods.p_line - mp.row;
  898.              i > 0 && header_upline(0); 
  899.              --i)
  900.               ;
  901.         }
  902.         else {
  903.             for (i = mp.row-ods.p_line;
  904.              i > 0 && header_downline(!hdr_only, 0);
  905.              --i)
  906.               ;
  907.         }
  908.  
  909.         if((ods.p_off = mp.col - headents[ods.cur_e].prlen) <= 0)
  910.           ods.p_off = 0;
  911.  
  912.         /* -3 is returned  if we drop out bottom
  913.          * *and* want to process a mousepress.  The Headereditor
  914.          * wrapper should make sense of this return code.
  915.          */
  916.             if (ods.p_line >= ComposerTopLine)
  917.             retval = -3;
  918.         }
  919.         break;
  920. #endif /* MOUSE */
  921.  
  922.       case (CTRL|'T') :            /* Call field selector */
  923.             if(headents[ods.cur_e].is_attach) {
  924.                 /*--- selector for attachments ----*/
  925.         char dir[NLINE], fn[NLINE], sz[NLINE];
  926.  
  927.         strcpy(dir, gmode&MDCURDIR ? "."
  928.                        : gmode&MDTREE ? opertree
  929.                               : gethomedir(NULL));
  930.         fn[0] = '\0';
  931.         if(FileBrowse(dir, fn, sz, FB_READ) == 1){ /* got a new file */
  932.             char buf[NLINE];
  933.             sprintf(buf, "%s%c%s (%s) \"\"%s", dir, C_FILESEP, fn, sz, 
  934.                 (!headents[ods.cur_e].hd_text->text[0]) ? "":",");
  935.             if(FormatLines(headents[ods.cur_e].hd_text, buf,
  936.                    term.t_ncol - headents[ods.cur_e].prlen,
  937.                    headents[ods.cur_e].break_on_comma,0)==-1){
  938.             emlwrite("\007Format lines failed!", NULL);
  939.             }
  940.  
  941.             UpdateHeader();
  942.         }                /* else, nothing of interest */
  943.             } else if (headents[ods.cur_e].selector != NULL) {
  944.                 /*---- General selector for non-attachments -----*/
  945.                 errmss = NULL;
  946.                 bufp = (*(headents[ods.cur_e].selector))(&errmss);
  947.         ttresize();            /* fixup screen bufs */
  948.         picosigs();            /* restore altered signals */
  949.                 if(bufp != NULL) {
  950.                     if(headents[ods.cur_e].break_on_comma) {
  951.                         /*--- Must be an address ---*/
  952.                         if(ods.cur_l->text[0] != '\0'){
  953.                 for(i = ++ods.p_len; i; i--)
  954.                   ods.cur_l->text[i] = ods.cur_l->text[i-1];
  955.  
  956.                 ods.cur_l->text[0] = ',';
  957.             }
  958.  
  959.                         if(FormatLines(ods.cur_l, bufp,
  960.                       (term.t_ncol-headents[ods.cur_e].prlen), 
  961.                                       headents[ods.cur_e].break_on_comma,
  962.                       0) == -1){
  963.                             emlwrite("Problem adding address to header !",
  964.                                      NULL);
  965.                             (*term.t_beep)();
  966.                             break;
  967.                         }
  968.  
  969.             /*
  970.              * If the "selector" has a "builder" as well, pass
  971.              * what was just selected thru the builder...
  972.              */
  973.             if(headents[ods.cur_e].builder){
  974.                 struct hdr_line *l;
  975.                 int             cur_row, top_too = 0;
  976.  
  977.                 for(l = headents[ods.cur_e].hd_text, cur_row = 0;
  978.                 l && l != ods.cur_l;
  979.                 l = l->next, cur_row++)
  980.                   ;
  981.  
  982.                 top_too = ods.cur_l == ods.top_l;
  983.  
  984.                 if(call_builder(&headents[ods.cur_e]) < 0){
  985.                 emlwrite("Call builder failed!", NULL);
  986.                 }
  987.  
  988.                 for(ods.cur_l = headents[ods.cur_e].hd_text;
  989.                 ods.cur_l->next && cur_row;
  990.                 ods.cur_l = ods.cur_l->next, cur_row--)
  991.                   ;
  992.  
  993.                 if(top_too)
  994.                   ods.top_l = ods.cur_l;
  995.             }
  996.  
  997.                     UpdateHeader();
  998.             free(bufp);
  999.                     } else {
  1000.                         strcpy(headents[ods.cur_e].hd_text->text, bufp);
  1001.                     }
  1002.             /* mark this entry dirty */
  1003.             headents[ods.cur_e].sticky = 1;
  1004.             headents[ods.cur_e].dirty  = 1;
  1005.                     PaintBody(0); /* Repaint entire screen */
  1006.         } else {
  1007.                     PaintBody(0); /* Repaint entire screen,then error msg */
  1008.                     if(errmss != NULL) {
  1009.                         (*term.t_beep)();
  1010.                     emlwrite(errmss, NULL);
  1011.                     }
  1012.                 }
  1013.         } else {
  1014.                 /*----- No selector -----*/
  1015.         (*term.t_beep)();
  1016.         continue;
  1017.         }
  1018.         PaintBody(0);
  1019.         continue;
  1020.  
  1021.       case (CTRL|'G'):            /* HELP */
  1022.         if(term.t_mrow == 0){
  1023.         if(km_popped == 0){
  1024.             km_popped = 2;
  1025.             sgarbk = 1;            /* bring up menu */
  1026.             break;
  1027.         }
  1028.         }
  1029.  
  1030.         if(!ComposerHelp(ods.cur_e))
  1031.           break;                /* else, fall through... */
  1032.  
  1033.       case (CTRL|'L'):            /* redraw requested */
  1034.         PaintBody(0);
  1035.         break;
  1036.  
  1037.       default :                /* huh? */
  1038. bleep:
  1039.         if(ch&CTRL)
  1040.           emlwrite("\007Unknown command: ^%c", (void *)(ch&0xff));
  1041.         else
  1042.       case BADESC:
  1043.           emlwrite("\007Unknown command", NULL);
  1044.  
  1045.       case NODATA:
  1046.         break;
  1047.     }
  1048.     }
  1049.     while (ods.p_line < ComposerTopLine);
  1050.  
  1051.     display_delimiter(1);
  1052.     curwp->w_flag |= WFMODE;
  1053.     movecursor(currow, curcol);
  1054.     ComposerEditing = FALSE;
  1055.     return(retval);
  1056. }
  1057.  
  1058.  
  1059. /*
  1060.  *
  1061.  */
  1062. int
  1063. header_downline(beyond, gripe)
  1064.     int beyond, gripe;
  1065. {
  1066.     struct hdr_line *new_l;
  1067.     int    new_e, status, fullpaint, len;
  1068.  
  1069.     /* calculate the next line: physical *and* logical */
  1070.     status    = 0;
  1071.     new_e     = ods.cur_e;
  1072.     if((new_l = next_hline(&new_e, ods.cur_l)) == NULL && !beyond){
  1073.     if(gripe)
  1074.       emlwrite("Can't move down. Use ^X to eXit/Save.", NULL);
  1075.  
  1076.         return(0);
  1077.     }
  1078.  
  1079.     fullpaint = ++ods.p_line >= BOTTOM();    /* force full redraw?       */
  1080.  
  1081.     /* expand what needs expanding */
  1082.     if(new_e != ods.cur_e || !new_l){        /* new (or last) field !    */
  1083.     if(new_l)
  1084.       InvertPrompt(ods.cur_e, FALSE);    /* turn off current entry   */
  1085.  
  1086.     if(headents[ods.cur_e].is_attach) {    /* verify data ?        */
  1087.         if(status = SyncAttach()){        /* fixup if 1 or -1        */
  1088.         headents[ods.cur_e].rich_header = 0;
  1089.         if(FormatLines(headents[ods.cur_e].hd_text, "",
  1090.                    term.t_ncol-headents[new_e].prlen,
  1091.                    headents[ods.cur_e].break_on_comma, 0) == -1)
  1092.           emlwrite("\007Format lines failed!", NULL);
  1093.         }
  1094.     } else if(headents[ods.cur_e].builder) { /* expand addresses        */
  1095.         if((status = call_builder(&headents[ods.cur_e])) > 0){
  1096.         struct hdr_line *l;        /* fixup ods.cur_l */
  1097.         ods.p_line = 0;            /* force top line recalc */
  1098.         for(l = headents[ods.cur_e].hd_text; l; l = l->next)
  1099.           ods.cur_l = l;
  1100.  
  1101.         if(new_l)            /* if new_l, force validity */
  1102.           new_l = headents[new_e].hd_text;
  1103.  
  1104.         NewTop();            /* get new top_l */
  1105.         }
  1106.         else if(status < 0){        /* bad addr? no leave! */
  1107.         --ods.p_line;
  1108.         InvertPrompt(ods.cur_e, TRUE);
  1109.         return(0);
  1110.         }
  1111.     }
  1112.  
  1113.     if(new_l){                /* if one below, turn it on */
  1114.         InvertPrompt(new_e, TRUE);
  1115.         sgarbk = 1;                /* paint keymenu too        */
  1116.     }
  1117.     }
  1118.  
  1119.     if(new_l){                    /* fixup new pointers        */
  1120.     ods.cur_l = (ods.cur_e != new_e) ? headents[new_e].hd_text : new_l;
  1121.     ods.cur_e = new_e;
  1122.     if(ods.p_off > (len = strlen(ods.cur_l->text)))
  1123.       ods.p_off = len;
  1124.     }
  1125.  
  1126.     if(!new_l || status || fullpaint){        /* handle big screen paint  */
  1127.     UpdateHeader();
  1128.     PaintHeader(COMPOSER_TOP_LINE, FALSE);
  1129.     PaintBody(1);
  1130.  
  1131.     if(!new_l){                /* make sure we're done     */
  1132.         ods.p_line = ComposerTopLine;
  1133.         InvertPrompt(ods.cur_e, FALSE);    /* turn off current entry   */
  1134.     }
  1135.     }
  1136.  
  1137.     return(new_l ? 1 : 0);
  1138. }
  1139.  
  1140.  
  1141. /*
  1142.  *
  1143.  */
  1144. int
  1145. header_upline(gripe)
  1146.     int gripe;
  1147. {
  1148.     struct hdr_line *new_l;
  1149.     int    new_e, status, fullpaint, len;
  1150.  
  1151.     /* calculate the next line: physical *and* logical */
  1152.     status    = 0;
  1153.     fullpaint = ods.p_line-- == COMPOSER_TOP_LINE;
  1154.     new_e     = ods.cur_e;
  1155.     if(!(new_l = prev_hline(&new_e, ods.cur_l))){    /* all the way up! */
  1156.     ods.p_line = COMPOSER_TOP_LINE;
  1157.     if(gripe)
  1158.       emlwrite("Can't move beyond top of %s",
  1159.           (Pmaster->pine_flags & P_ABOOK) ? "entry" : "header");
  1160.  
  1161.     return(0);
  1162.     }
  1163.  
  1164.     if(new_e != ods.cur_e){            /* new field ! */
  1165.     InvertPrompt(ods.cur_e, FALSE);
  1166.     if(headents[ods.cur_e].is_attach){
  1167.         if(status = SyncAttach()){        /* non-zero ? reformat field */
  1168.         headents[ods.cur_e].rich_header = 0;
  1169.         if(FormatLines(headents[ods.cur_e].hd_text, "",
  1170.                    term.t_ncol - headents[ods.cur_e].prlen,
  1171.                    headents[ods.cur_e].break_on_comma,0) == -1)
  1172.           emlwrite("\007Format lines failed!", NULL);
  1173.         }
  1174.     }
  1175.     else if(headents[ods.cur_e].builder){
  1176.         if((status = call_builder(&headents[ods.cur_e])) >= 0){
  1177.         /* repair new_l */
  1178.         for(new_l = headents[new_e].hd_text;
  1179.             new_l->next;
  1180.             new_l=new_l->next)
  1181.           ;
  1182.         }
  1183.         else{
  1184.         ++ods.p_line;
  1185.         InvertPrompt(ods.cur_e, TRUE);
  1186.         return(0);
  1187.         }
  1188.     }
  1189.  
  1190.     InvertPrompt(new_e, TRUE);
  1191.     sgarbk = 1;
  1192.     }
  1193.  
  1194.     ods.cur_e = new_e;                /* update pointers */
  1195.     ods.cur_l = new_l;
  1196.     if(ods.p_off > (len = strlen(ods.cur_l->text)))
  1197.       ods.p_off = len;
  1198.  
  1199.     if(status > 0 || fullpaint){
  1200.     UpdateHeader();
  1201.     PaintHeader(COMPOSER_TOP_LINE, FALSE);
  1202.     PaintBody(1);
  1203.     }
  1204.  
  1205.     return(1);
  1206. }
  1207.  
  1208.  
  1209. /*
  1210.  * 
  1211.  */
  1212. int
  1213. AppendAttachment(fn, sz, cmt)
  1214.     char *fn, *sz, *cmt;
  1215. {
  1216.     int     a_e, status;
  1217.     struct hdr_line *lp;
  1218.  
  1219.     /*--- Find headerentry that is attachments (only first) --*/
  1220.     for(a_e = 0; headents[a_e].name != NULL; a_e++ )
  1221.       if(headents[a_e].is_attach){
  1222.       /* make sure field stays displayed */
  1223.       headents[a_e].rich_header = 0;
  1224.       headents[a_e].display_it = 1;
  1225.       break;
  1226.       }
  1227.  
  1228.     /* append new attachment line */
  1229.     for(lp = headents[a_e].hd_text; lp->next; lp=lp->next)
  1230.       ;
  1231.  
  1232.     /* build new attachment line */
  1233.     if(lp->text[0]){        /* adding a line? */
  1234.     strcat(lp->text, ",");    /* append delimiter */
  1235.     if(lp->next = HALLOC()){    /* allocate new line */
  1236.         lp->next->prev = lp;
  1237.         lp->next->next = NULL;
  1238.         lp = lp->next;
  1239.     }
  1240.     else{
  1241.         emlwrite("\007Can't allocate line for new attachment!", NULL);
  1242.         return(0);
  1243.     }
  1244.     }
  1245.  
  1246.     sprintf(lp->text, "%s (%s) \"%.*s\"", fn,
  1247.         sz ? sz : "", 80, cmt ? cmt : "");
  1248.  
  1249.     /* validate the new attachment, and reformat if needed */
  1250.     if(status = SyncAttach()){
  1251.     if(status < 0)
  1252.       emlwrite("\007Problem attaching: %s", fn);
  1253.  
  1254.     if(FormatLines(headents[a_e].hd_text, "",
  1255.                term.t_ncol - headents[a_e].prlen,
  1256.                headents[a_e].break_on_comma, 0) == -1){
  1257.         emlwrite("\007Format lines failed!", NULL);
  1258.         return(0);
  1259.     }
  1260.     }
  1261.  
  1262.     UpdateHeader();
  1263.     PaintHeader(COMPOSER_TOP_LINE, status != 0);
  1264.     PaintBody(1);
  1265.     return(status != 0);
  1266. }
  1267.  
  1268.  
  1269.  
  1270.  
  1271. /*
  1272.  * LineEdit - the idea is to manage 7 bit ascii character only input.
  1273.  *            Always use insert mode and handle line wrapping
  1274.  *
  1275.  *    returns:
  1276.  *        Any characters typed in that aren't printable 
  1277.  *        (i.e. commands)
  1278.  *
  1279.  *    notes: 
  1280.  *        Assume we are guaranteed that there is sufficiently 
  1281.  *        more buffer space in a line than screen width (just one 
  1282.  *        less thing to worry about).  If you want to change this,
  1283.  *        then pputc will have to be taught to check the line buffer
  1284.  *        length, and HALLOC() will probably have to become a func.
  1285.  */
  1286. LineEdit(allowedit)
  1287. int    allowedit;
  1288. {
  1289.     register struct    hdr_line   *lp;        /* temporary line pointer    */
  1290.     register int    i;
  1291.     register int    ch = 0;
  1292.     register int    status;            /* various func's return val */
  1293.     register char    *tbufp;            /* temporary buffer pointers */
  1294.          int    skipmove = 0;
  1295.              char    *strng;
  1296.  
  1297.     strng   = ods.cur_l->text;            /* initialize offsets */
  1298.     ods.p_len = strlen(strng);
  1299.     if(ods.p_off < 0)                /* offset within range? */
  1300.       ods.p_off = 0;
  1301.     else if(ods.p_off > ods.p_len)
  1302.       ods.p_off = ods.p_len;
  1303.     else if(ods.p_off > LINELEN())        /* shouldn't happen, but */
  1304.         ods.p_off = LINELEN();            /* you never know...     */
  1305.  
  1306.     while(1){                    /* edit the line... */
  1307.  
  1308.     if(skipmove)
  1309.       skipmove = 0;
  1310.     else
  1311.       HeaderPaintCursor();
  1312.  
  1313.     last_key = ch;
  1314.  
  1315.     (*term.t_flush)();            /* get everything out */
  1316.  
  1317. #ifdef MOUSE
  1318.     mouse_in_content(K_MOUSE, -1, -1, 0, 0);
  1319.     register_mfunc(mouse_in_content,2,0,term.t_nrow-(term.t_mrow+1),
  1320.                term.t_ncol);
  1321. #endif
  1322. #ifdef    _WINDOWS
  1323.     { extern int composer_file_drop();
  1324.     mswin_setdndcallback (composer_file_drop);
  1325.     }
  1326. #endif
  1327.  
  1328.         ch = GetKey();
  1329.  
  1330. #ifdef    MOUSE
  1331.     clear_mfunc(mouse_in_content);
  1332. #endif
  1333. #ifdef    _WINDOWS
  1334.     mswin_cleardndcallback ();
  1335. #endif
  1336.  
  1337.     if(ch == DEL && (gmode & P_DELRUBS))
  1338.       ch = K_PAD_DELETE;
  1339.        
  1340.     if(ch == NODATA || time_to_check()){    /* new mail ? */
  1341.         if((*Pmaster->newmail)(ch == NODATA ? 0 : 2, 1) >= 0){
  1342.         if(km_popped){
  1343.             term.t_mrow = 2;
  1344.             curwp->w_ntrows -= 2;
  1345.         }
  1346.  
  1347.         clearcursor();
  1348.         mlerase();
  1349.         (*Pmaster->showmsg)(ch);
  1350.         mpresf = 1;
  1351.         if(km_popped){
  1352.             term.t_mrow = 0;
  1353.             curwp->w_ntrows += 2;
  1354.         }
  1355.         }
  1356.  
  1357.         clearcursor();
  1358.         movecursor(ods.p_line, ods.p_off+headents[ods.cur_e].prlen);
  1359.         if(ch == NODATA)            /* GetKey timed out */
  1360.           continue;
  1361.     }
  1362.  
  1363.         if(mpresf){                /* blast old messages */
  1364.         if(mpresf++ > NMMESSDELAY){        /* every few keystrokes */
  1365.         mlerase();
  1366.         movecursor(ods.p_line, ods.p_off+headents[ods.cur_e].prlen);
  1367.         }
  1368.         }
  1369.  
  1370.     if(VALID_KEY(ch)){            /* char input */
  1371.             /*
  1372.              * if we are allowing editing, insert the new char
  1373.              * end up leaving tbufp pointing to newly
  1374.              * inserted character in string, and offset to the
  1375.              * index of the character after the inserted ch ...
  1376.              */
  1377.             if(allowedit){
  1378.         if(headents[ods.cur_e].only_file_chars
  1379.            && !fallowc((unsigned char) ch)){
  1380.             /* no garbage in filenames */
  1381.             emlwrite("\007Can't have a '%c' in folder name",
  1382.                  (void *) ch);
  1383.             continue;
  1384.         }
  1385.         else if(headents[ods.cur_e].is_attach
  1386.             && intag(strng,ods.p_off)){
  1387.             emlwrite("\007Can't edit attachment number!", NULL);
  1388.             continue;
  1389.         }
  1390.  
  1391.         if(headents[ods.cur_e].single_space){
  1392.             if(ch == ' ' 
  1393.                && (strng[ods.p_off]==' ' || strng[ods.p_off-1]==' '))
  1394.               continue;
  1395.         }
  1396.  
  1397.         /*
  1398.          * go ahead and add the character...
  1399.          */
  1400.         tbufp = &strng[++ods.p_len];    /* find the end */
  1401.         do{
  1402.             *tbufp = tbufp[-1];
  1403.         } while(--tbufp > &strng[ods.p_off]);    /* shift right */
  1404.         strng[ods.p_off++] = ch;    /* add char to str */
  1405.  
  1406.         /* mark this entry dirty */
  1407.         headents[ods.cur_e].sticky = 1;
  1408.         headents[ods.cur_e].dirty  = 1;
  1409.  
  1410.         /*
  1411.          * then find out where things fit...
  1412.          */
  1413.         if(ods.p_len < LINELEN()){
  1414.             CELL c;
  1415.  
  1416.             c.c = ch;
  1417.             c.a = 0;
  1418.             if(pinsert(c)){        /* add char to str */
  1419.             skipmove++;        /* must'a been optimal */
  1420.             continue;         /* on to the next! */
  1421.             }
  1422.         }
  1423.         else{
  1424.                     if((status = FormatLines(ods.cur_l, "", LINELEN(), 
  1425.                         headents[ods.cur_e].break_on_comma,0)) == -1){
  1426.                         (*term.t_beep)();
  1427.                         continue;
  1428.                     }
  1429.                     else{
  1430.             /*
  1431.              * during the format, the dot may have moved
  1432.              * down to the next line...
  1433.              */
  1434.             if(ods.p_off >= strlen(strng)){
  1435.                 ods.p_line++;
  1436.                 ods.p_off -= strlen(strng);
  1437.                 ods.cur_l = ods.cur_l->next;
  1438.                 strng = ods.cur_l->text;
  1439.             }
  1440.             ods.p_len = strlen(strng);
  1441.             }
  1442.             UpdateHeader();
  1443.             PaintHeader(COMPOSER_TOP_LINE, FALSE);
  1444.             PaintBody(1);
  1445.                     continue;
  1446.         }
  1447.             }
  1448.             else{  
  1449.                 rdonly();
  1450.                 continue;
  1451.             } 
  1452.         }
  1453.         else {                    /* interpret ch as a command */
  1454.             switch (ch = normalize_cmd(ch, ckm, 2)) {
  1455.           case (CTRL|'@') :        /* word skip */
  1456.         while(strng[ods.p_off]
  1457.               && isalnum((unsigned char)strng[ods.p_off]))
  1458.           ods.p_off++;        /* skip any text we're in */
  1459.  
  1460.         while(strng[ods.p_off]
  1461.               && !isalnum((unsigned char)strng[ods.p_off]))
  1462.           ods.p_off++;        /* skip any whitespace after it */
  1463.  
  1464.         if(strng[ods.p_off] == '\0'){
  1465.             ods.p_off = 0;    /* end of line, let caller handle it */
  1466.             return(K_PAD_DOWN);
  1467.         }
  1468.  
  1469.         continue;
  1470.  
  1471.           case (CTRL|'K') :            /* kill line cursor's on */
  1472.         if(!allowedit){
  1473.             rdonly();
  1474.             continue;
  1475.         }
  1476.  
  1477.         lp = ods.cur_l;
  1478.         ods.p_off = 0;
  1479.  
  1480.         if(lp->next && lp->prev)
  1481.           ods.cur_l = next_hline(&ods.cur_e, lp);
  1482.         else if(lp->prev)
  1483.           ods.cur_l = prev_hline(&ods.cur_e, lp);
  1484.  
  1485.         if(lp == ods.top_l)
  1486.           ods.top_l = ods.cur_l;
  1487.  
  1488.         if(KillHeaderLine(lp, (last_key == (CTRL|'K')))){
  1489.             if(optimize && 
  1490.                !(ods.cur_l->prev==NULL && ods.cur_l->next==NULL))
  1491.               scrollup(wheadp, ods.p_line, 1);
  1492.  
  1493.             if(ods.cur_l->next == NULL)
  1494.               zotcomma(ods.cur_l->text);
  1495.             
  1496.             i = (ods.p_line == COMPOSER_TOP_LINE);
  1497.             UpdateHeader();
  1498.             PaintHeader(i ? COMPOSER_TOP_LINE: ods.p_line, FALSE);
  1499.             if(km_popped){
  1500.             km_popped--;
  1501.             movecursor(term.t_nrow, 0);
  1502.             peeol();
  1503.             }
  1504.  
  1505.             PaintBody(1);
  1506.         }
  1507.         strng = ods.cur_l->text;
  1508.         ods.p_len = strlen(strng);
  1509.         headents[ods.cur_e].sticky = 0;
  1510.         headents[ods.cur_e].dirty  = 1;
  1511.         continue;
  1512.  
  1513.           case (CTRL|'U') :            /* un-delete deleted lines */
  1514.         if(!allowedit){
  1515.             rdonly();
  1516.             continue;
  1517.         }
  1518.  
  1519.         if(SaveHeaderLines()){
  1520.             UpdateHeader();
  1521.             PaintHeader(COMPOSER_TOP_LINE, FALSE);
  1522.             if(km_popped){
  1523.             km_popped--;
  1524.             movecursor(term.t_nrow, 0);
  1525.             peeol();
  1526.             }
  1527.  
  1528.             PaintBody(1);
  1529.  
  1530.             ods.p_off = 0;        /* dot hasn't moved! */
  1531.             strng = ods.cur_l->text;
  1532.             ods.p_len = strlen(strng);
  1533.             headents[ods.cur_e].sticky = 1;
  1534.             headents[ods.cur_e].dirty  = 1;
  1535.         }
  1536.         else
  1537.           emlwrite("Problem Unkilling text", NULL);
  1538.         continue;
  1539.  
  1540.           case (CTRL|'F') :
  1541.           case K_PAD_RIGHT:            /* move character right */
  1542.         if(ods.p_off < ods.p_len){
  1543.             pputc(pscr(ods.p_line, 
  1544.                    (ods.p_off++)+headents[ods.cur_e].prlen)->c,0);
  1545.             skipmove++;
  1546.             continue;
  1547.         }
  1548.         else if(gmode & MDHDRONLY)
  1549.           continue;
  1550.  
  1551.         ods.p_off = 0;
  1552.         return(K_PAD_DOWN);
  1553.  
  1554.           case (CTRL|'B') :
  1555.           case K_PAD_LEFT    :        /* move character left */
  1556.         if(ods.p_off > 0){
  1557.             ods.p_off--;
  1558.             continue;
  1559.         }
  1560.         if(ods.p_line != COMPOSER_TOP_LINE)
  1561.           ods.p_off = 1000;        /* put cursor at end of line */
  1562.         return(K_PAD_UP);
  1563.  
  1564.           case (CTRL|'M') :            /* goto next field */
  1565.         ods.p_off = 0;
  1566.         return(K_PAD_DOWN);
  1567.  
  1568.           case K_PAD_HOME :
  1569.           case (CTRL|'A') :            /* goto beginning of line */
  1570.         ods.p_off = 0;
  1571.         continue;
  1572.  
  1573.           case K_PAD_END  :
  1574.           case (CTRL|'E') :            /* goto end of line */
  1575.         ods.p_off = ods.p_len;
  1576.         continue;
  1577.  
  1578.           case (CTRL|'D')   :        /* blast this char */
  1579.           case K_PAD_DELETE :
  1580.         if(!allowedit){
  1581.             rdonly();
  1582.             continue;
  1583.         }
  1584.         else if(ods.p_off >= strlen(strng))
  1585.           continue;
  1586.  
  1587.         if(headents[ods.cur_e].is_attach && intag(strng, ods.p_off)){
  1588.             emlwrite("\007Can't edit attachment number!", NULL);
  1589.             continue;
  1590.         }
  1591.  
  1592.         pputc(strng[ods.p_off++], 0);     /* drop through and rubout */
  1593.  
  1594.           case DEL        :            /* blast previous char */
  1595.           case (CTRL|'H') :
  1596.         if(!allowedit){
  1597.             rdonly();
  1598.             continue;
  1599.         }
  1600.  
  1601.         if(headents[ods.cur_e].is_attach && intag(strng, ods.p_off-1)){
  1602.             emlwrite("\007Can't edit attachment number!", NULL);
  1603.             continue;
  1604.         }
  1605.  
  1606.         if(ods.p_off > 0){        /* just shift left one char */
  1607.             ods.p_len--;
  1608.             headents[ods.cur_e].dirty  = 1;
  1609.             if(ods.p_len == 0)
  1610.               headents[ods.cur_e].sticky = 0;
  1611.             else
  1612.               headents[ods.cur_e].sticky = 1;
  1613.  
  1614.             tbufp = &strng[--ods.p_off];
  1615.             while(*tbufp++ != '\0')
  1616.               tbufp[-1] = *tbufp;
  1617.             tbufp = &strng[ods.p_off];
  1618.             if(pdel())            /* physical screen delete */
  1619.               skipmove++;        /* must'a been optimal */
  1620.         }
  1621.         else{                /* may have work to do */
  1622.             if(ods.cur_l->prev == NULL){  
  1623.             (*term.t_beep)();    /* no erase into next field */
  1624.             continue;
  1625.             }
  1626.  
  1627.             ods.p_line--;
  1628.             ods.cur_l = ods.cur_l->prev;
  1629.             strng = ods.cur_l->text;
  1630.             if((i=strlen(strng)) > 0){
  1631.             strng[i-1] = '\0';    /* erase the character */
  1632.             ods.p_off = i-1;
  1633.             }
  1634.             else{
  1635.             headents[ods.cur_e].sticky = 0;
  1636.             ods.p_off = 0;
  1637.             }
  1638.             
  1639.             tbufp = &strng[ods.p_off];
  1640.         }
  1641.  
  1642.         if((status = FormatLines(ods.cur_l, "", LINELEN(), 
  1643.                    headents[ods.cur_e].break_on_comma,0))==-1){
  1644.             (*term.t_beep)();
  1645.             continue;
  1646.         }
  1647.         else{
  1648.             /*
  1649.              * beware, the dot may have moved...
  1650.              */
  1651.             while((ods.p_len=strlen(strng)) < ods.p_off){
  1652.             ods.p_line++;
  1653.             ods.p_off -= strlen(strng);
  1654.             ods.cur_l = ods.cur_l->next;
  1655.             strng = ods.cur_l->text;
  1656.             ods.p_len = strlen(strng);
  1657.             tbufp = &strng[ods.p_off];
  1658.             status = TRUE;
  1659.             }
  1660.             UpdateHeader();
  1661.             PaintHeader(COMPOSER_TOP_LINE, FALSE);
  1662.             if(status == TRUE)
  1663.               PaintBody(1);
  1664.         }
  1665.  
  1666.         movecursor(ods.p_line, ods.p_off+headents[ods.cur_e].prlen);
  1667.  
  1668.         if(skipmove)
  1669.           continue;
  1670.  
  1671.         break;
  1672.  
  1673.               default   :
  1674.         return(ch);
  1675.             }
  1676.         }
  1677.  
  1678.     while (*tbufp != '\0')        /* synchronizing loop */
  1679.       pputc(*tbufp++, 0);
  1680.  
  1681.     if(ods.p_len < LINELEN())
  1682.       peeol();
  1683.  
  1684.     }
  1685. }
  1686.  
  1687.  
  1688. int
  1689. HeaderPaintCursor()
  1690. {
  1691.     movecursor(ods.p_line, ods.p_off+headents[ods.cur_e].prlen);
  1692. }
  1693.  
  1694.  
  1695.  
  1696. /*
  1697.  * FormatLines - Place the given text at the front of the given line->text
  1698.  *               making sure to properly format the line, then check
  1699.  *               all lines below for proper format.
  1700.  *
  1701.  *    notes:
  1702.  *        Not much optimization at all.  Right now, it recursively
  1703.  *        fixes all remaining lines in the entry.  Some speed might
  1704.  *        gained if this was built to iteratively scan the lines.
  1705.  *
  1706.  *    returns:
  1707.  *        -1 on error
  1708.  *        FALSE if only this line is changed
  1709.  *        TRUE  if text below the first line is changed
  1710.  */
  1711. FormatLines(h, instr, maxlen, break_on_comma, quoted)
  1712. struct  hdr_line  *h;                /* where to begin formatting */
  1713. char    *instr;                    /* input string */
  1714. int    maxlen;                    /* max chars on a line */
  1715. int    break_on_comma;                /* break lines on commas */
  1716. int    quoted;                    /* this line inside quotes */
  1717. {
  1718.     int        retval = FALSE;
  1719.     register    int    i, l;
  1720.     char    *ostr;                /* pointer to output string */
  1721.     register    char    *breakp;        /* pointer to line break */
  1722.     register    char    *bp, *tp;        /* temporary pointers */
  1723.     char    *buf;                /* string to add later */
  1724.     struct hdr_line    *nlp, *lp;
  1725.  
  1726.     ostr = h->text;
  1727.     nlp = h->next;
  1728.     l = strlen(instr) + strlen(ostr);
  1729.     if((buf = (char *)malloc(l+10)) == NULL)
  1730.       return(-1);
  1731.  
  1732.     if(l >= maxlen){                /* break then fixup below */
  1733.     if(strlen(instr) < maxlen){        /* room for more */
  1734.  
  1735.         if(break_on_comma && (bp = (char *)strqchr(instr, ',', "ed))){
  1736.         bp += (bp[1] == ' ') ? 2 : 1;
  1737.         for(tp = bp; *tp && *tp == ' '; tp++)
  1738.           ;
  1739.  
  1740.         strcpy(buf, tp);
  1741.         strcat(buf, ostr);
  1742.         for(i = 0; &instr[i] < bp; i++)
  1743.           ostr[i] = instr[i];
  1744.         ostr[i] = '\0';
  1745.         retval = TRUE;
  1746.         }
  1747.         else{
  1748.         breakp = break_point(ostr, maxlen-strlen(instr),
  1749.                      break_on_comma ? ',' : ' ',
  1750.                      break_on_comma ? "ed : NULL);
  1751.  
  1752.         if(breakp == ostr){    /* no good breakpoint */
  1753.             if(break_on_comma && *breakp == ','){
  1754.             breakp = ostr + 1;
  1755.             retval = TRUE;
  1756.             }
  1757.             else if(strchr(instr,(break_on_comma && !quoted)?',':' ')){
  1758.             strcpy(buf, ostr);
  1759.             strcpy(ostr, instr);
  1760.             }
  1761.             else{        /* instr's broken as we can get it */
  1762.             breakp = &ostr[maxlen-strlen(instr)-1];
  1763.             retval = TRUE;
  1764.             }
  1765.         }
  1766.         else
  1767.           retval = TRUE;
  1768.         
  1769.         if(retval){
  1770.             strcpy(buf, breakp);    /* save broken line  */
  1771.             if(breakp == ostr){
  1772.             strcpy(ostr, instr);    /* simple if no break */
  1773.             }
  1774.             else{
  1775.             *breakp = '\0';        /* more work to break it */
  1776.             i = strlen(instr);
  1777.             /*
  1778.              * shift ostr i chars
  1779.              */
  1780.             for(bp=breakp; bp >= ostr && i; bp--)
  1781.               *(bp+i) = *bp;
  1782.             for(tp=ostr, bp=instr; *bp != '\0'; tp++, bp++)
  1783.               *tp = *bp;        /* then add instr */
  1784.             }
  1785.         }
  1786.         }
  1787.     }
  1788.     else{                    /* instr > maxlen ! */
  1789.         if(break_on_comma){
  1790.         breakp = (!(bp = strqchr(instr, ',', "ed))
  1791.               || bp - instr >= maxlen)
  1792.                ? &instr[maxlen]
  1793.                : bp + ((bp[1] == ' ') ? 2 : 1);
  1794.         }
  1795.         else{
  1796.         breakp = break_point(instr, maxlen, ' ', NULL);
  1797.  
  1798.         if(breakp == instr)        /* no good break point */
  1799.           breakp = &instr[maxlen - 1];
  1800.         }
  1801.         
  1802.         strcpy(buf, breakp);        /* save broken line */
  1803.         strcat(buf, ostr);            /* add line that was there */
  1804.         for(tp=ostr,bp=instr; bp < breakp; tp++, bp++)
  1805.           *tp = *bp;
  1806.  
  1807.         *tp = '\0';
  1808.     }
  1809.  
  1810.     if(nlp == NULL){            /* no place to add below? */
  1811.         if((lp = HALLOC()) == NULL){
  1812.         emlwrite("Can't allocate any more lines for header!", NULL);
  1813.         free(buf);
  1814.         return(-1);
  1815.         }
  1816.  
  1817.         if(optimize && (i = physical_line(h)) != -1)
  1818.           scrolldown(wheadp, i - 1, 1);
  1819.  
  1820.         h->next = lp;            /* fix up links */
  1821.         lp->prev = h;
  1822.         lp->next = NULL;
  1823.         lp->text[0] = '\0';
  1824.         nlp = lp;
  1825.         retval = TRUE;
  1826.     }
  1827.     else
  1828.         retval = FALSE;
  1829.     }
  1830.     else{                    /* combined length < max */
  1831.     if(*instr){
  1832.         strcpy(buf, instr);            /* insert instr before ostr */
  1833.         strcat(buf, ostr);
  1834.         strcpy(ostr, buf);
  1835.     }
  1836.  
  1837.     *buf = '\0';
  1838.     breakp = NULL;
  1839.  
  1840.     if(break_on_comma && (breakp = strqchr(ostr, ',', "ed))){
  1841.         breakp += (breakp[1] == ' ') ? 2 : 1;
  1842.         strcpy(buf, breakp);
  1843.         *breakp = '\0';
  1844.  
  1845.         if(strlen(buf) && !nlp){
  1846.         if((lp = HALLOC()) == NULL){
  1847.             emlwrite("Can't allocate any more lines for header!",NULL);
  1848.             free(buf);
  1849.             return(-1);
  1850.         }
  1851.  
  1852.         if(optimize && (i = physical_line(h)) != -1)
  1853.           scrolldown(wheadp, i - 1, 1);
  1854.  
  1855.         h->next = lp;        /* fix up links */
  1856.         lp->prev = h;
  1857.         lp->next = NULL;
  1858.         lp->text[0] = '\0';
  1859.         nlp = lp;
  1860.         retval = TRUE;
  1861.         }
  1862.     }
  1863.  
  1864.     if(nlp){
  1865.         if(!strlen(buf) && !breakp){
  1866.         if(strlen(ostr) + strlen(nlp->text) >= maxlen){
  1867.             breakp = break_point(nlp->text, maxlen-strlen(ostr), 
  1868.                      break_on_comma ? ',' : ' ',
  1869.                      break_on_comma ? "ed : NULL);
  1870.             
  1871.             if(breakp == nlp->text){    /* commas this line? */
  1872.             for(tp=ostr; *tp  && *tp != ' '; tp++)
  1873.               ;
  1874.  
  1875.             if(!*tp){        /* no commas, get next best */
  1876.                 breakp += maxlen - strlen(ostr) - 1;
  1877.                 retval = TRUE;
  1878.             }
  1879.             else
  1880.               retval = FALSE;
  1881.             }
  1882.             else
  1883.               retval = TRUE;
  1884.  
  1885.             if(retval){            /* only if something to do */
  1886.             for(tp = &ostr[strlen(ostr)],bp=nlp->text; bp<breakp; 
  1887.             tp++, bp++)
  1888.               *tp = *bp;        /* add breakp to this line */
  1889.             *tp = '\0';
  1890.             for(tp=nlp->text, bp=breakp; *bp != '\0'; tp++, bp++)
  1891.               *tp = *bp;        /* shift next line to left */
  1892.             *tp = '\0';
  1893.             }
  1894.         }
  1895.         else{
  1896.             strcat(ostr, nlp->text);
  1897.  
  1898.             if(optimize && (i = physical_line(nlp)) != -1)
  1899.               scrollup(wheadp, i, 1);
  1900.  
  1901.             hldelete(nlp);
  1902.  
  1903.             if(!(nlp = h->next)){
  1904.             free(buf);
  1905.             return(TRUE);        /* can't go further */
  1906.             }
  1907.             else
  1908.               retval = TRUE;        /* more work to do? */
  1909.         }
  1910.         }
  1911.     }
  1912.     else{
  1913.         free(buf);
  1914.         return(FALSE);
  1915.     }
  1916.  
  1917.     }
  1918.  
  1919.     i = FormatLines(nlp, buf, maxlen, break_on_comma, quoted);
  1920.     free(buf);
  1921.     switch(i){
  1922.       case -1:                    /* bubble up worst case */
  1923.     return(-1);
  1924.       case FALSE:
  1925.     if(retval == FALSE)
  1926.       return(FALSE);
  1927.       default:
  1928.     return(TRUE);
  1929.     }
  1930. }
  1931.  
  1932.  
  1933.  
  1934. /*
  1935.  * PaintHeader - do the work of displaying the header from the given 
  1936.  *               physical screen line the end of the header.
  1937.  *
  1938.  *       17 July 91 - fixed reshow to deal with arbitrarily large headers.
  1939.  */
  1940. void
  1941. PaintHeader(line, clear)
  1942.     int    line;                    /* physical line on screen   */
  1943.     int    clear;                    /* clear before painting */
  1944. {
  1945.     register struct hdr_line    *lp;
  1946.     register char    *bufp;
  1947.     register int    curline;
  1948.     register int    curoffset;
  1949.     char     buf[NLINE];
  1950.     int      e;
  1951.  
  1952.     if(clear)
  1953.       pclear(COMPOSER_TOP_LINE, ComposerTopLine);
  1954.  
  1955.     curline   = COMPOSER_TOP_LINE;
  1956.     curoffset = 0;
  1957.  
  1958.     for(lp = ods.top_l, e = ods.top_e; ; curline++){
  1959.     if((curline == line) || ((lp = next_hline(&e, lp)) == NULL))
  1960.       break;
  1961.     }
  1962.  
  1963.     while(headents[e].name != NULL){            /* begin to redraw */
  1964.     while(lp != NULL){
  1965.         buf[0] = '\0';
  1966.             if((!lp->prev || curline == COMPOSER_TOP_LINE) && !curoffset){
  1967.             if(InvertPrompt(e, (e == ods.cur_e && ComposerEditing)) == -1
  1968.            && !is_blank(curline, 0, headents[e].prlen))
  1969.            sprintf(buf, "%*s", headents[e].prlen, " ");
  1970.         }
  1971.         else if(!is_blank(curline, 0, headents[e].prlen))
  1972.           sprintf(buf, "%*s", headents[e].prlen, " ");
  1973.  
  1974.         if(*(bufp = buf) != '\0'){        /* need to paint? */
  1975.         movecursor(curline, 0);        /* paint the line... */
  1976.         while(*bufp != '\0')
  1977.           pputc(*bufp++, 0);
  1978.         }
  1979.  
  1980.         bufp = &(lp->text[curoffset]);    /* skip chars already there */
  1981.         curoffset += headents[e].prlen;
  1982.         while(*bufp == pscr(curline, curoffset)->c && *bufp != '\0'){
  1983.         bufp++;
  1984.         if(++curoffset >= term.t_ncol)
  1985.           break;
  1986.         }
  1987.  
  1988.         if(*bufp != '\0'){            /* need to move? */
  1989.         movecursor(curline, curoffset);
  1990.         while(*bufp != '\0'){        /* display what's not there */
  1991.             pputc(*bufp++, 0);
  1992.             curoffset++;
  1993.         }
  1994.         }
  1995.  
  1996.         if(curoffset < term.t_ncol 
  1997.            && !is_blank(curline, curoffset, term.t_ncol - curoffset)){
  1998.         movecursor(curline, curoffset);
  1999.         peeol();
  2000.         }
  2001.         curline++;
  2002.  
  2003.             curoffset = 0;
  2004.         if(curline >= BOTTOM())
  2005.           break;
  2006.  
  2007.         lp = lp->next;
  2008.         }
  2009.  
  2010.     if(curline >= BOTTOM())
  2011.       return;                /* don't paint delimiter */
  2012.  
  2013.     while(headents[++e].name != NULL)
  2014.       if(headents[e].display_it){
  2015.           lp = headents[e].hd_text;
  2016.           break;
  2017.       }
  2018.     }
  2019.  
  2020.     display_delimiter(ComposerEditing ? 0 : 1);
  2021. }
  2022.  
  2023.  
  2024.  
  2025.  
  2026. /*
  2027.  * PaintBody() - generic call to handle repainting everything BUT the 
  2028.  *         header
  2029.  *
  2030.  *    notes:
  2031.  *        The header redrawing in a level 0 body paint gets done
  2032.  *        in update()
  2033.  */
  2034. PaintBody(level)
  2035. int    level;
  2036. {
  2037.     curwp->w_flag |= WFHARD;            /* make sure framing's right */
  2038.     if(level == 0)                /* specify what to update */
  2039.         sgarbf = TRUE;
  2040.  
  2041.     update();                    /* display message body */
  2042.  
  2043.     if(level == 0 && ComposerEditing){
  2044.     mlerase();                /* clear the error line */
  2045.     ShowPrompt();
  2046.     }
  2047. }
  2048.  
  2049.  
  2050. /*
  2051.  * display_for_send - paint the composer from the top line and return.
  2052.  */
  2053. void
  2054. display_for_send()
  2055. {
  2056.     int             i = 0;
  2057.     struct hdr_line *l;
  2058.  
  2059.     /* if first header line isn't displayed, there's work to do */
  2060.     if(headents && ((l = first_hline(&i)) != ods.top_l
  2061.             || ComposerTopLine == COMPOSER_TOP_LINE
  2062.             || !ods.p_line)){
  2063.     struct on_display orig_ods;
  2064.     int          orig_edit    = ComposerEditing,
  2065.               orig_ct_line = ComposerTopLine;
  2066.  
  2067.     /*
  2068.      * fake that the cursor's in the first header line
  2069.      * and force repaint...
  2070.      */
  2071.     orig_ods    = ods;
  2072.     ods.cur_e    = i;
  2073.     ods.top_l    = ods.cur_l = l;
  2074.     ods.top_e    = ods.cur_e;
  2075.     ods.p_line    = COMPOSER_TOP_LINE;
  2076.     ComposerEditing = TRUE;            /* to fool update() */
  2077.     setimark(FALSE, 1);            /* remember where we were */
  2078.     gotobob(FALSE, 1);
  2079.  
  2080.     UpdateHeader();                /* redraw whole enchilada */
  2081.     PaintHeader(COMPOSER_TOP_LINE, TRUE);
  2082.     PaintBody(0);
  2083.  
  2084.     ods = orig_ods;                /* restore original state */
  2085.     ComposerEditing = orig_edit;
  2086.     ComposerTopLine = curwp->w_toprow = orig_ct_line;
  2087.         curwp->w_ntrows = BOTTOM() - ComposerTopLine;
  2088.     swapimark(FALSE, 1);
  2089.  
  2090.     /* in case we don't exit, set up restoring the screen */
  2091.     sgarbf = TRUE;                /* force redraw */
  2092.     }
  2093. }
  2094.  
  2095.  
  2096. /*
  2097.  * ArrangeHeader - set up display parm such that header is reasonably 
  2098.  *                 displayed
  2099.  */
  2100. ArrangeHeader()
  2101. {
  2102.     int      e;
  2103.     register struct hdr_line *l;
  2104.  
  2105.     ods.p_line = ods.p_off = 0;
  2106.     e = ods.top_e = 0;
  2107.     l = ods.top_l = headents[e].hd_text;
  2108.     while(headents[e+1].name || (l && l->next))
  2109.       if(l = next_hline(&e, l)){
  2110.       ods.cur_l = l;
  2111.       ods.cur_e = e;
  2112.       }
  2113.  
  2114.     UpdateHeader();
  2115. }
  2116.  
  2117.  
  2118. /*
  2119.  * ComposerHelp() - display mail help in a context sensitive way
  2120.  *                  based on the level passed ...
  2121.  */
  2122. ComposerHelp(level)
  2123. int    level;
  2124. {
  2125.     char buf[80];
  2126.  
  2127.     curwp->w_flag |= WFMODE;
  2128.     sgarbf = TRUE;
  2129.  
  2130.     if(level < 0 || !headents[level].name){
  2131.     (*term.t_beep)();
  2132.     emlwrite("Sorry, I can't help you with that.", NULL);
  2133.     sleep(2);
  2134.     return(FALSE);
  2135.     }
  2136.  
  2137.     sprintf(buf, "Help for %s %.40s Field",
  2138.          (Pmaster->pine_flags & P_ABOOK) ? "Address Book"
  2139.                          : "Composer",
  2140.          headents[level].name);
  2141.     (*Pmaster->helper)(headents[level].help, buf, 1);
  2142.     ttresize();
  2143.     picosigs();                    /* restore altered handlers */
  2144.     return(TRUE);
  2145. }
  2146.  
  2147.  
  2148.  
  2149. /*
  2150.  * ToggleHeader() - set or unset pico values to the full screen size
  2151.  *                  painting header if need be.
  2152.  */
  2153. ToggleHeader(show)
  2154. int show;
  2155. {
  2156.     /*
  2157.      * check to see if we need to display the header... 
  2158.      */
  2159.     if(show){
  2160.     UpdateHeader();                /* figure bounds  */
  2161.     PaintHeader(COMPOSER_TOP_LINE, FALSE);    /* draw it */
  2162.     }
  2163.     else{
  2164.         /*
  2165.          * set bounds for no header display
  2166.          */
  2167.         curwp->w_toprow = ComposerTopLine = COMPOSER_TOP_LINE;
  2168.         curwp->w_ntrows = BOTTOM() - ComposerTopLine;
  2169.     }
  2170.     return(TRUE);
  2171. }
  2172.  
  2173.  
  2174.  
  2175. /*
  2176.  * HeaderLen() - return the length in lines of the exposed portion of the
  2177.  *               header
  2178.  */
  2179. HeaderLen()
  2180. {
  2181.     register struct hdr_line *lp;
  2182.     int      e;
  2183.     int      i;
  2184.     
  2185.     i = 1;
  2186.     lp = ods.top_l;
  2187.     e  = ods.top_e;
  2188.     while(lp != NULL){
  2189.     lp = next_hline(&e, lp);
  2190.     i++;
  2191.     }
  2192.     return(i);
  2193. }
  2194.  
  2195.  
  2196.  
  2197. /*
  2198.  * first_hline() - return a pointer to the first displayable header line
  2199.  * 
  2200.  *    returns:
  2201.  *        1) pointer to first displayable line in header and header
  2202.  *                 entry, via side effect, that the first line is a part of
  2203.  *              2) NULL if no next line, leaving entry at LASTHDR
  2204.  */
  2205. struct hdr_line *
  2206. first_hline(entry)
  2207.     int *entry;
  2208. {
  2209.     /* init *entry so we're sure to start from the top */
  2210.     for(*entry = 0; headents[*entry].name; (*entry)++)
  2211.       if(headents[*entry].display_it)
  2212.     return(headents[*entry].hd_text);
  2213.  
  2214.     *entry = 0;
  2215.     return(NULL);        /* this shouldn't happen */
  2216. }
  2217.  
  2218.  
  2219.  
  2220. /*
  2221.  * next_hline() - return a pointer to the next line structure
  2222.  * 
  2223.  *    returns:
  2224.  *        1) pointer to next displayable line in header and header
  2225.  *                 entry, via side effect, that the next line is a part of
  2226.  *              2) NULL if no next line, leaving entry at LASTHDR
  2227.  */
  2228. struct hdr_line *
  2229. next_hline(entry, line)
  2230.     int *entry;
  2231.     struct hdr_line *line;
  2232. {
  2233.     if(line == NULL)
  2234.       return(NULL);
  2235.  
  2236.     if(line->next == NULL){
  2237.     while(headents[++(*entry)].name != NULL){
  2238.         if(headents[*entry].display_it)
  2239.           return(headents[*entry].hd_text);
  2240.     }
  2241.     --(*entry);
  2242.     return(NULL);
  2243.     }
  2244.     else
  2245.       return(line->next);
  2246. }
  2247.  
  2248.  
  2249.  
  2250. /*
  2251.  * prev_hline() - return a pointer to the next line structure back
  2252.  * 
  2253.  *    returns:
  2254.  *              1) pointer to previous displayable line in header and 
  2255.  *                 the header entry that the next line is a part of 
  2256.  *                 via side effect
  2257.  *              2) NULL if no next line, leaving entry unchanged from
  2258.  *                 the value it had on entry.
  2259.  */
  2260. struct hdr_line *
  2261. prev_hline(entry, line)
  2262.     int *entry;
  2263.     struct hdr_line *line;
  2264. {
  2265.     if(line == NULL)
  2266.       return(NULL);
  2267.  
  2268.     if(line->prev == NULL){
  2269.     int orig_entry;
  2270.  
  2271.     orig_entry = *entry;
  2272.     while(--(*entry) >= 0){
  2273.         if(headents[*entry].display_it){
  2274.         line = headents[*entry].hd_text;
  2275.         while(line->next != NULL)
  2276.           line = line->next;
  2277.         return(line);
  2278.         }
  2279.     }
  2280.  
  2281.     *entry = orig_entry;
  2282.     return(NULL);
  2283.     }
  2284.     else
  2285.       return(line->prev);
  2286. }
  2287.  
  2288.  
  2289.  
  2290. /*
  2291.  * first_requested_hline() - return pointer to first line that pico's caller
  2292.  *                 asked that we start on.
  2293.  */
  2294. struct hdr_line *
  2295. first_requested_hline(ent)
  2296.     int *ent;
  2297. {
  2298.     int             i, reqfield;
  2299.     struct hdr_line *rv = NULL;
  2300.  
  2301.     for(reqfield = -1, i = 0; headents[i].name;  i++)
  2302.       if(headents[i].start_here){
  2303.       headents[i].start_here = 0;        /* clear old setting */
  2304.       if(reqfield < 0){            /* if not already, set up */
  2305.           headents[i].display_it = 1;    /* make sure it's shown */
  2306.           *ent = reqfield = i;
  2307.           rv = headents[i].hd_text;
  2308.       }
  2309.       }
  2310.  
  2311.     return(rv);
  2312. }
  2313.  
  2314.  
  2315.  
  2316. /*
  2317.  * UpdateHeader() - determines the best range of lines to be displayed 
  2318.  *                  using the global ods value for the current line and the
  2319.  *            top line, also sets ComposerTopLine and pico limits
  2320.  *                    
  2321.  *      notes:
  2322.  *            This is pretty ugly because it has to keep the current line
  2323.  *        on the screen in a reasonable location no matter what.
  2324.  *        There are also a couple of rules to follow:
  2325.  *                 1) follow paging conventions of pico (ie, half page 
  2326.  *              scroll)
  2327.  *                 2) if more than one page, always display last half when 
  2328.  *                    pline is toward the end of the header
  2329.  * 
  2330.  *      returns:
  2331.  *             TRUE  if anything changed (side effects: new p_line, top_l
  2332.  *             top_e, and pico parms)
  2333.  *             FALSE if nothing changed 
  2334.  *             
  2335.  */
  2336. UpdateHeader()
  2337. {
  2338.     register struct    hdr_line    *lp;
  2339.     int         i, le;
  2340.     int      ret = FALSE;
  2341.     int      old_top = ComposerTopLine;
  2342.     int      old_p = ods.p_line;
  2343.  
  2344.     if(ods.p_line < COMPOSER_TOP_LINE || ods.p_line >= BOTTOM()){
  2345.     NewTop();                /* get new top_l */
  2346.     ret = TRUE;
  2347.     }
  2348.     else{                    /* make sure p_line's OK */
  2349.     i = COMPOSER_TOP_LINE;
  2350.     lp = ods.top_l;
  2351.     le = ods.top_e;
  2352.     while(lp != ods.cur_l){
  2353.         /*
  2354.          * this checks to make sure cur_l is below top_l and that
  2355.          * cur_l is on the screen...
  2356.          */
  2357.         if((lp = next_hline(&le, lp)) == NULL || ++i >= BOTTOM()){
  2358.         NewTop();
  2359.         ret = TRUE;
  2360.         break;
  2361.         }
  2362.     }
  2363.     }
  2364.  
  2365.     ods.p_line = COMPOSER_TOP_LINE;        /* find  p_line... */
  2366.     lp = ods.top_l;
  2367.     le = ods.top_e;
  2368.     while(lp && lp != ods.cur_l){
  2369.     lp = next_hline(&le, lp);
  2370.     ods.p_line++;
  2371.     }
  2372.  
  2373.     if(!ret)
  2374.       ret = !(ods.p_line == old_p);
  2375.  
  2376.     ComposerTopLine = ods.p_line;        /* figure top composer line */
  2377.     while(lp && ComposerTopLine <= BOTTOM()){
  2378.     lp = next_hline(&le, lp);
  2379.     ComposerTopLine += (lp) ? 1 : 2;    /* allow for delim at end   */
  2380.     }
  2381.  
  2382.     if(!ret)
  2383.       ret = !(ComposerTopLine == old_top);
  2384.  
  2385.     if(wheadp->w_toprow != ComposerTopLine){    /* update pico params... */
  2386.         wheadp->w_toprow = ComposerTopLine;
  2387.         wheadp->w_ntrows = ((i = BOTTOM() - ComposerTopLine) > 0) ? i : 0;
  2388.     ret = TRUE;
  2389.     }
  2390.     return(ret);
  2391. }
  2392.  
  2393.  
  2394.  
  2395. /*
  2396.  * NewTop() - calculate a new top_l based on the cur_l
  2397.  *
  2398.  *    returns:
  2399.  *        with ods.top_l and top_e pointing at a reasonable line
  2400.  *        entry
  2401.  */
  2402. NewTop()
  2403. {
  2404.     register struct hdr_line *lp;
  2405.     register int i;
  2406.     int      e;
  2407.  
  2408.     lp = ods.cur_l;
  2409.     e  = ods.cur_e;
  2410.     i  = HALF_SCR();
  2411.  
  2412.     while(lp != NULL && i--){
  2413.     ods.top_l = lp;
  2414.     ods.top_e = e;
  2415.     lp = prev_hline(&e, lp);
  2416.     }
  2417. }
  2418.  
  2419.  
  2420.  
  2421. /*
  2422.  * display_delimiter() - just paint the header/message body delimiter with
  2423.  *                       inverse value specified by state.
  2424.  */
  2425. void
  2426. display_delimiter(state)
  2427. int    state;
  2428. {
  2429.     register char    *bufp;
  2430.     static   short   ps   = 0;            /* previous state */
  2431.  
  2432.     if(ComposerTopLine - 1 >= BOTTOM())        /* silently forget it */
  2433.       return;
  2434.  
  2435.     bufp = (gmode & MDHDRONLY) ? "" : HDR_DELIM;
  2436.  
  2437.     if(state == ps){                /* optimize ? */
  2438.     for(ps = 0; bufp[ps] && pscr(ComposerTopLine-1,ps)->c == bufp[ps];ps++)
  2439.       ;
  2440.  
  2441.     if(bufp[ps] == '\0' && !(gmode & MDHDRONLY)){
  2442.         ps = state;
  2443.         return;                /* already displayed! */
  2444.     }
  2445.     }
  2446.  
  2447.     ps = state;
  2448.  
  2449.     movecursor(ComposerTopLine - 1, 0);
  2450.     if(state)
  2451.       (*term.t_rev)(1);
  2452.  
  2453.     while(*bufp != '\0')
  2454.       pputc(*bufp++, 0);
  2455.  
  2456.     if(state)
  2457.       (*term.t_rev)(0);
  2458.  
  2459.     peeol();
  2460. }
  2461.  
  2462.  
  2463.  
  2464. /*
  2465.  * InvertPrompt() - invert the prompt associated with header entry to state
  2466.  *                  state (true if invert, false otherwise).
  2467.  *    returns:
  2468.  *        non-zero if nothing done
  2469.  *        0 if prompt inverted successfully
  2470.  *
  2471.  *    notes:
  2472.  *        come to think of it, this func and the one above could
  2473.  *        easily be combined
  2474.  */
  2475. InvertPrompt(entry, state)
  2476. int    entry, state;
  2477. {
  2478.     register char   *bufp;
  2479.     register int    i;
  2480.     static   short  ps = 0;             /* prev state of entry e */
  2481.  
  2482.     bufp = headents[entry].prompt;        /* fresh prompt paint */
  2483.     if((i = entry_line(entry, FALSE)) == -1)
  2484.       return(-1);                /* silently forget it */
  2485.  
  2486.     if((ps&(1<<entry)) == (state ? 1<<entry : 0)){    /* optimize ? */
  2487.     int j;
  2488.  
  2489.     for(j = 0; bufp[j] && pscr(i, j)->c == bufp[j]; j++)
  2490.       ;
  2491.  
  2492.     if(bufp[j] == '\0'){
  2493.         if(state)
  2494.           ps |= 1<<entry;
  2495.         else
  2496.           ps &= ~(1<<entry);
  2497.         return(0);                /* already displayed! */
  2498.     }
  2499.     }
  2500.  
  2501.     if(state)
  2502.       ps |= 1<<entry;
  2503.     else
  2504.       ps &= ~(1<<entry);
  2505.  
  2506.     movecursor(i, 0);
  2507.     if(state)
  2508.       (*term.t_rev)(1);
  2509.  
  2510.     while(*bufp && *(bufp + 1))
  2511.       pputc(*bufp++, 1);            /* putc upto last char */
  2512.  
  2513.     if(state)
  2514.       (*term.t_rev)(0);
  2515.  
  2516.     pputc(*bufp, 0);                /* last char not inverted */
  2517.     return(TRUE);
  2518. }
  2519.  
  2520.  
  2521.  
  2522.  
  2523. /*
  2524.  * partial_entries() - toggle display of the bcc and fcc fields.
  2525.  *
  2526.  *    returns:
  2527.  *        TRUE if there are partial entries on the display
  2528.  *        FALSE otherwise.
  2529.  */
  2530. partial_entries()
  2531. {
  2532.     register struct headerentry *h;
  2533.     int                          is_on;
  2534.   
  2535.     /*---- find out status of first rich header ---*/
  2536.     for(h = headents; !h->rich_header && h->name != NULL; h++)
  2537.       ;
  2538.  
  2539.     is_on = h->display_it;
  2540.     for(h = headents; h->name != NULL; h++) 
  2541.       if(h->rich_header) 
  2542.         h->display_it = ! is_on;
  2543.  
  2544.     return(is_on);
  2545. }
  2546.  
  2547.  
  2548.  
  2549. /*
  2550.  * entry_line() - return the physical line on the screen associated
  2551.  *                with the given header entry field.  Note: the field
  2552.  *                may span lines, so if the last char is set, return
  2553.  *                the appropriate value.
  2554.  *
  2555.  *    returns:
  2556.  *             1) physical line number of entry
  2557.  *             2) -1 if entry currently not on display
  2558.  */
  2559. entry_line(entry, lastchar)
  2560. int    entry, lastchar;
  2561. {
  2562.     register int    p_line = COMPOSER_TOP_LINE;
  2563.     int    i;
  2564.     register struct hdr_line    *line;
  2565.  
  2566.     for(line = ods.top_l, i = ods.top_e;
  2567.     headents && headents[i].name && i <= entry;
  2568.     p_line++){
  2569.     if(p_line >= BOTTOM())
  2570.       break;
  2571.     if(i == entry){
  2572.         if(lastchar){
  2573.         if(line->next == NULL)
  2574.           return(p_line);
  2575.         }
  2576.         else if(line->prev == NULL)
  2577.           return(p_line);
  2578.         else
  2579.           return(-1);
  2580.     }
  2581.     line = next_hline(&i, line);
  2582.     }
  2583.     return(-1);
  2584. }
  2585.  
  2586.  
  2587.  
  2588. /*
  2589.  * physical_line() - return the physical line on the screen associated
  2590.  *                   with the given header line pointer.
  2591.  *
  2592.  *    returns:
  2593.  *             1) physical line number of entry
  2594.  *             2) -1 if entry currently not on display
  2595.  */
  2596. physical_line(l)
  2597. struct hdr_line *l;
  2598. {
  2599.     register int    p_line = COMPOSER_TOP_LINE;
  2600.     register struct hdr_line    *lp;
  2601.     int    i;
  2602.  
  2603.     for(lp=ods.top_l, i=ods.top_e; headents[i].name && lp != NULL; p_line++){
  2604.     if(p_line >= BOTTOM())
  2605.       break;
  2606.  
  2607.     if(lp == l)
  2608.       return(p_line);
  2609.  
  2610.     lp = next_hline(&i, lp);
  2611.     }
  2612.     return(-1);
  2613. }
  2614.  
  2615.  
  2616.  
  2617. /*
  2618.  * call_builder() - resolve any nicknames in the address book associated
  2619.  *                  with the given entry...
  2620.  *
  2621.  *    NOTES:
  2622.  * 
  2623.  *      BEWARE: this function can cause cur_l and top_l to get lost so BE 
  2624.  *              CAREFUL before and after you call this function!!!
  2625.  * 
  2626.  *      There could to be something here to resolve cur_l and top_l
  2627.  *      reasonably into the new linked list for this entry.  
  2628.  *
  2629.  *      The reason this would mostly work without it is resolve_niks gets
  2630.  *      called for the most part in between fields.  Since we're moving
  2631.  *      to the beginning or end (i.e. the next/prev pointer in the old 
  2632.  *      freed cur_l is NULL) of the next entry, we get a new cur_l
  2633.  *      pointing at a good line.  Then since top_l is based on cur_l in
  2634.  *      NewTop() we have pretty much lucked out.
  2635.  * 
  2636.  *      Where we could get burned is in a canceled exit (ctrl|x).  Here
  2637.  *      nicknames get resolved into addresses, which invalidates cur_l
  2638.  *      and top_l.  Since we don't actually leave, we could begin editing
  2639.  *      again with bad pointers.  This would usually results in a nice 
  2640.  *      core dump.
  2641.  *
  2642.  *    RETURNS:
  2643.  *              > 0 if any names where resolved, otherwise
  2644.  *                0 if not, or
  2645.  *        < 0 on error
  2646.  *                -1: move to next line
  2647.  *                -2: don't move off this line
  2648.  */
  2649. call_builder(entry)
  2650. struct headerentry *entry;
  2651. {
  2652.     register    int     retval = 0;
  2653.     register    int    i;
  2654.     register    struct  hdr_line  *line;
  2655.     char    *sbuf;
  2656.     char    *errmsg, *s = NULL, *fcc = NULL;
  2657.     struct headerentry *e;
  2658.     BUILDER_ARG *nextarg, *arg = NULL, *headarg = NULL;
  2659.  
  2660.     if(!entry->builder)
  2661.       return(0);
  2662.  
  2663.     line = entry->hd_text;
  2664.     i = 0;
  2665.     while(line != NULL){
  2666.     i += term.t_ncol;
  2667.         line = line->next;
  2668.     }
  2669.     
  2670.     if((sbuf=(char *)malloc((unsigned) i)) == NULL){
  2671.     emlwrite("Can't malloc space to expand address", NULL);
  2672.     return(-1);
  2673.     }
  2674.     
  2675.     *sbuf = '\0';
  2676.     /*
  2677.      * cat the whole entry into one string...
  2678.      */
  2679.     line = entry->hd_text;
  2680.     while(line != NULL){
  2681.     i = strlen(line->text);
  2682.     /*
  2683.      * to keep pine address builder happy, addresses should be separated
  2684.      * by ", ".  Add this space if needed, otherwise...
  2685.      *
  2686.      * if this line is NOT a continuation of the previous line, add
  2687.      * white space for pine's address builder if its not already there...
  2688.      *
  2689.      * also if it's not a continuation (i.e., there's already and addr on 
  2690.      * the line), and there's another line below, treat the new line as
  2691.      * an implied comma
  2692.      */
  2693.         if(i && line->text[i-1] == ',')
  2694.       strcat(line->text, " ");        /* help address builder */
  2695.     else if(line->next != NULL && !strend(line->text, ',')){
  2696.         if(strqchr(line->text, ',', NULL))
  2697.           strcat(line->text, ", ");        /* implied comma */
  2698.     }
  2699.     else if(line->prev != NULL && line->next != NULL){
  2700.         if(strchr(line->prev->text, ' ') != NULL 
  2701.            && line->text[i-1] != ' ')
  2702.           strcat(line->text, " ");
  2703.     }
  2704.  
  2705.     strcat(sbuf, line->text);
  2706.         line = line->next;
  2707.     }
  2708.  
  2709.     if(entry->affected_entry){
  2710.     /* check if any non-sticky affected entries */
  2711.     for(e = entry->affected_entry; e; e = e->next_affected)
  2712.       if(!e->sticky)
  2713.         break;
  2714.  
  2715.     /* there is at least one non-sticky so make a list to pass */
  2716.     if(e){
  2717.         for(e = entry->affected_entry; e; e = e->next_affected){
  2718.         if(!arg){
  2719.             headarg = arg = (BUILDER_ARG *)malloc(sizeof(BUILDER_ARG));
  2720.             if(!arg){
  2721.             emlwrite("Can't malloc space for fcc", NULL);
  2722.             return(-1);
  2723.             }
  2724.             else{
  2725.             arg->next = NULL;
  2726.             arg->tptr = NULL;
  2727.             arg->xtra = &(e->bldr_private);
  2728.             }
  2729.         }
  2730.         else{
  2731.             nextarg = (BUILDER_ARG *)malloc(sizeof(BUILDER_ARG));
  2732.             if(!nextarg){
  2733.             emlwrite("Can't malloc space for fcc", NULL);
  2734.             return(-1);
  2735.             }
  2736.             else{
  2737.             nextarg->next = NULL;
  2738.             nextarg->tptr = NULL;
  2739.             nextarg->xtra = &(e->bldr_private);
  2740.             arg->next     = nextarg;
  2741.             arg           = arg->next;
  2742.             }
  2743.         }
  2744.  
  2745.         if(!e->sticky){
  2746.             line = e->hd_text;
  2747.             if(!(arg->tptr=(char *)malloc(strlen(line->text) + 1))){
  2748.             emlwrite("Can't malloc space for fcc", NULL);
  2749.             return(-1);
  2750.             }
  2751.             else
  2752.               strcpy(arg->tptr, line->text);
  2753.         }
  2754.         }
  2755.     }
  2756.     }
  2757.  
  2758.     errmsg = NULL;
  2759.  
  2760.     retval = (*entry->builder)(sbuf, &s, &errmsg, headarg);
  2761.  
  2762.     if(errmsg){
  2763.     if(*errmsg){
  2764.         char err[500];
  2765.  
  2766.         sprintf(err, "%s field: %s", entry->name, errmsg);
  2767.         (*term.t_beep)();
  2768.         emlwrite(err, NULL);
  2769.     }
  2770.     else
  2771.         mlerase();
  2772.  
  2773.     free(errmsg);
  2774.     }
  2775.  
  2776.     if(retval >= 0){
  2777.     if(strcmp(sbuf, s)){
  2778.         line = entry->hd_text;
  2779.         InitEntryText(s, entry);        /* arrange new one */
  2780.         zotentry(line);             /* blast old list o'entries */
  2781.         retval = 1;
  2782.     }
  2783.  
  2784.     if(headarg){
  2785.         for(e = entry->affected_entry, arg = headarg;
  2786.         e;
  2787.         e = e->next_affected, arg = arg->next){
  2788.         if(!e->sticky){
  2789.             line = e->hd_text;
  2790.             if(strcmp(line->text, arg->tptr)){ /* it changed */
  2791.             /* make sure they see it if changed */
  2792.             e->display_it = 1;
  2793.             InitEntryText(arg->tptr, e);
  2794.             if(line == ods.top_l)
  2795.               ods.top_l = e->hd_text;
  2796.  
  2797.             zotentry(line);    /* blast old list o'entries */
  2798.             e->dirty = 1;    /* mark it dirty */
  2799.             retval = 1;
  2800.             }
  2801.         }
  2802.         }
  2803.     }
  2804.     }
  2805.  
  2806.     if(s)
  2807.       free(s);
  2808.  
  2809.     if(headarg){
  2810.     for(arg = headarg; arg; arg = nextarg){
  2811.         /* Don't free xtra, it's just pointing to a headerentry member */
  2812.         nextarg = arg->next;
  2813.         if(arg->tptr)
  2814.           free(arg->tptr);
  2815.         
  2816.         free(arg);
  2817.     }
  2818.     }
  2819.  
  2820.     free(sbuf);
  2821.     return(retval);
  2822. }
  2823.  
  2824.  
  2825. /*
  2826.  * strend - neglecting white space, returns TRUE if c is at the
  2827.  *          end of the given line.  otherwise FALSE.
  2828.  */
  2829. strend(s, ch)
  2830. char *s;
  2831. int   ch;
  2832. {
  2833.     register char *b;
  2834.     register char  c;
  2835.  
  2836.     c = (char)ch;
  2837.  
  2838.     if(s == NULL)
  2839.       return(FALSE);
  2840.  
  2841.     if(*s == '\0')
  2842.       return(FALSE);
  2843.  
  2844.     b = &s[strlen(s)];
  2845.     while(isspace((unsigned char)(*--b))){
  2846.     if(b == s)
  2847.       return(FALSE);
  2848.     }
  2849.  
  2850.     return(*b == c);
  2851. }
  2852.  
  2853.  
  2854. /*
  2855.  * strqchr - returns pointer to first non-quote-enclosed occurance of c in 
  2856.  *           the given string.  otherwise NULL.
  2857.  */
  2858. char *
  2859. strqchr(s, ch, q)
  2860.     char *s;
  2861.     int   ch;
  2862.     int  *q;
  2863. {
  2864.     int     quoted = (q) ? *q : 0;
  2865.  
  2866.     for(; s && *s; s++){
  2867.     if(*s == '"'){
  2868.         quoted = !quoted;
  2869.         if(q)
  2870.           *q = quoted;
  2871.     }
  2872.  
  2873.     if(!quoted && *s == ch)
  2874.       return(s);
  2875.     }
  2876.  
  2877.     return(NULL);
  2878. }
  2879.  
  2880.  
  2881. /*
  2882.  * KillHeaderLine() - kill a line in the header
  2883.  *
  2884.  *    notes:
  2885.  *        This is pretty simple.  Just using the emacs kill buffer
  2886.  *        and its accompanying functions to cut the text from lines.
  2887.  *
  2888.  *    returns:
  2889.  *        TRUE if hldelete worked
  2890.  *        FALSE otherwise
  2891.  */
  2892. KillHeaderLine(l, append)
  2893. struct    hdr_line    *l;
  2894. int     append;
  2895. {
  2896.     register char    *c;
  2897.  
  2898.     if(!append)
  2899.     kdelete();
  2900.  
  2901.     c = l->text;
  2902.     while(*c != '\0')                /* splat out the line */
  2903.       kinsert(*c++);
  2904.  
  2905.     kinsert('\n');                /* helpful to yank in body */
  2906.  
  2907. #ifdef _WINDOWS
  2908.     mswin_killbuftoclip (kremove);
  2909. #endif
  2910.  
  2911.     return(hldelete(l));            /* blast it  */
  2912. }
  2913.  
  2914.  
  2915.  
  2916. /*
  2917.  * SaveHeaderLines() - insert the saved lines in the list before the 
  2918.  *                     current line in the header
  2919.  *
  2920.  *    notes:
  2921.  *        Once again, just using emacs' kill buffer and its 
  2922.  *              functions.
  2923.  *
  2924.  *    returns:
  2925.  *        TRUE if something good happend
  2926.  *        FALSE otherwise
  2927.  */
  2928. SaveHeaderLines()
  2929. {
  2930.     char     *buf;                /* malloc'd copy of buffer */
  2931.     register char       *bp;            /* pointer to above buffer */
  2932.     register unsigned    i;            /* index */
  2933.     
  2934.     if(ksize()){
  2935.     if((bp = buf = (char *)malloc(ksize()+5)) == NULL){
  2936.         emlwrite("Can't malloc space for saved text", NULL);
  2937.         return(FALSE);
  2938.     }
  2939.     }
  2940.     else
  2941.       return(FALSE);
  2942.  
  2943.     for(i=0; i < ksize(); i++)
  2944.       if(kremove(i) != '\n')            /* filter out newlines */
  2945.     *bp++ = kremove(i);
  2946.     *bp = '\0';
  2947.  
  2948.     while(--bp >= buf)                /* kill trailing white space */
  2949.       if(*bp != ' '){
  2950.       if(ods.cur_l->text[0] != '\0'){
  2951.           if(*bp == '>'){            /* inserting an address */
  2952.           *++bp = ',';            /* so add separator */
  2953.           *++bp = '\0';
  2954.           }
  2955.       }
  2956.       else{                    /* nothing in field yet */
  2957.           if(*bp == ','){            /* so blast any extra */
  2958.           *bp = '\0';            /* separators */
  2959.           }
  2960.       }
  2961.       break;
  2962.       }
  2963.  
  2964.     if(FormatLines(ods.cur_l, buf, LINELEN(),
  2965.            headents[ods.cur_e].break_on_comma, 0) == -1)
  2966.       i = FALSE;
  2967.     else
  2968.       i = TRUE;
  2969.  
  2970.     free(buf);
  2971.     return(i);
  2972. }
  2973.  
  2974.  
  2975.  
  2976.  
  2977. /*
  2978.  * break_point - Break the given line s at the most reasonable character c
  2979.  *               within l max characters.
  2980.  *
  2981.  *    returns:
  2982.  *        Pointer to the best break point in s, or
  2983.  *        Pointer to the beginning of s if no break point found
  2984.  */
  2985. char *
  2986. break_point(s, l, ch, q)
  2987.     char *s;
  2988.     int   l, ch, *q;
  2989. {
  2990.     register char *b = s + l;
  2991.     int            quoted = (q) ? *q : 0;
  2992.  
  2993.     while(b != s){
  2994.     if(ch == ',' && *b == '"')        /* don't break on quoted ',' */
  2995.       quoted = !quoted;            /* toggle quoted state */
  2996.  
  2997.     if(*b == ch){
  2998.         if(ch == ' '){
  2999.         if(b + 1 < s + l){
  3000.             b++;            /* leave the ' ' */
  3001.             break;
  3002.         }
  3003.         }
  3004.         else{
  3005.         /*
  3006.          * if break char isn't a space, leave a space after
  3007.          * the break char.
  3008.          */
  3009.         if(!(b+1 >= s+l || (b[1] == ' ' && b+2 == s+l))){
  3010.             b += (b[1] == ' ') ? 2 : 1;
  3011.             break;
  3012.         }
  3013.         }
  3014.     }
  3015.     b--;
  3016.     }
  3017.  
  3018.     if(q)
  3019.       *q = quoted;
  3020.  
  3021.     return((quoted) ? s : b);
  3022. }
  3023.  
  3024.  
  3025.  
  3026.  
  3027. /*
  3028.  * hldelete() - remove the header line pointed to by l from the linked list
  3029.  *              of lines.
  3030.  *
  3031.  *    notes:
  3032.  *        the case of first line in field is kind of bogus.  since
  3033.  *              the array of headers has a pointer to the first line, and 
  3034.  *        i don't want to worry about this too much, i just copied 
  3035.  *        the line below and removed it rather than the first one
  3036.  *        from the list.
  3037.  *
  3038.  *    returns:
  3039.  *        TRUE if it worked 
  3040.  *        FALSE otherwise
  3041.  */
  3042. hldelete(l)
  3043. struct hdr_line  *l;
  3044. {
  3045.     register struct hdr_line *lp;
  3046.  
  3047.     if(l == NULL)
  3048.       return(FALSE);
  3049.  
  3050.     if(l->next == NULL && l->prev == NULL){    /* only one line in field */
  3051.     l->text[0] = '\0';
  3052.     return(TRUE);                /* no free only line in list */
  3053.     }
  3054.     else if(l->next == NULL){            /* last line in field */
  3055.     l->prev->next = NULL;
  3056.     }
  3057.     else if(l->prev == NULL){            /* first line in field */
  3058.     strcpy(l->text, l->next->text);
  3059.     lp = l->next;
  3060.     if((l->next = lp->next) != NULL)
  3061.       l->next->prev = l;
  3062.     l = lp;
  3063.     }
  3064.     else{                    /* some where in field */
  3065.     l->prev->next = l->next;
  3066.     l->next->prev = l->prev;
  3067.     }
  3068.  
  3069.     l->next = NULL;
  3070.     l->prev = NULL;
  3071.     free((char *)l);
  3072.     return(TRUE);
  3073. }
  3074.  
  3075.  
  3076.  
  3077. /*
  3078.  * is_blank - returns true if the next n chars from coordinates row, col
  3079.  *           on display are spaces
  3080.  */
  3081. is_blank(row, col, n)
  3082. int row, col, n;
  3083. {
  3084.     n += col;
  3085.     for( ;col < n; col++){
  3086.     if(pscr(row, col)->c != ' ')
  3087.       return(0);
  3088.     }
  3089.     return(1);
  3090. }
  3091.  
  3092.  
  3093. /*
  3094.  * ShowPrompt - display key help corresponding to the current header entry
  3095.  */
  3096. ShowPrompt()
  3097. {
  3098.     int new_e = ods.cur_e;
  3099.  
  3100.     if(headents[ods.cur_e].key_label){
  3101.     menu_header[TO_KEY].name  = "^T";
  3102.     menu_header[TO_KEY].label = headents[ods.cur_e].key_label;
  3103.     KS_OSDATASET(&menu_header[TO_KEY], KS_OSDATAGET(&headents[ods.cur_e]));
  3104.     }
  3105.     else
  3106.       menu_header[TO_KEY].name  = NULL;
  3107.     
  3108.     if(gmode & (MDVIEW | MDHDRONLY)){
  3109.     menu_header[CUT_KEY].name  = NULL;
  3110.     menu_header[DEL_KEY].name  = NULL;
  3111.     menu_header[UDEL_KEY].name = NULL;
  3112.     menu_header[SEND_KEY].label =  (gmode & MDHDRONLY)
  3113.                      ? "eXit/Save" : "eXit";
  3114.     }
  3115.     else{
  3116.     menu_header[CUT_KEY].name  = "^K";
  3117.     menu_header[DEL_KEY].name  = "^D";
  3118.     menu_header[UDEL_KEY].name = "^U";
  3119.     menu_header[SEND_KEY].label = "Send";
  3120.     }
  3121.  
  3122.     if(gmode & MDHDRONLY){
  3123.     menu_header[RICH_KEY].label = "RichView";
  3124.     menu_header[PONE_KEY].name  = NULL;
  3125.     menu_header[ATT_KEY].name   = NULL;
  3126.     }
  3127.     else{
  3128.     menu_header[RICH_KEY].label = "Rich Hdr";
  3129.     menu_header[PONE_KEY].name  = "^O";
  3130.     menu_header[ATT_KEY].name   = "^J";
  3131.     }
  3132.  
  3133.     wkeyhelp(menu_header);
  3134. }
  3135.  
  3136.  
  3137. /*
  3138.  * packheader - packup all of the header fields for return to caller. 
  3139.  *              NOTE: all of the header info passed in, including address
  3140.  *                    of the pointer to each string is contained in the
  3141.  *                    header entry array "headents".
  3142.  */
  3143. packheader()
  3144. {
  3145.     register int    i = 0;        /* array index */
  3146.     register int    count;        /* count of chars in a field */
  3147.     register int    retval = TRUE;    /* count of chars in a field */
  3148.     register char    *bufp;        /* */
  3149.     register struct    hdr_line *line;
  3150.  
  3151.     if(!headents)
  3152.       return(TRUE);
  3153.  
  3154.     while(headents[i].name != NULL){
  3155. #ifdef    ATTACHMENTS
  3156.     /*
  3157.      * attachments are special case, already in struct we pass back
  3158.      */
  3159.     if(headents[i].is_attach){
  3160.         i++;
  3161.         continue;
  3162.     }
  3163. #endif
  3164.  
  3165.         /*
  3166.          * count chars to see if we need a new malloc'd space for our
  3167.          * array.
  3168.          */
  3169.         line = headents[i].hd_text;
  3170.         count = 0;
  3171.         while(line != NULL){
  3172.             /*
  3173.              * add one for possible concatination of a ' ' character ...
  3174.              */
  3175.             count += (strlen(line->text) + 1);
  3176.             line = line->next;
  3177.         }
  3178.         line = headents[i].hd_text;
  3179.         if(count < headents[i].maxlen){        
  3180.             *headents[i].realaddr[0] = '\0';
  3181.         }
  3182.         else{
  3183.             /*
  3184.              * don't forget to include space for the null terminator!!!!
  3185.              */
  3186.             if((bufp = (char *)malloc((count+1) * sizeof(char))) != NULL){
  3187.                 *bufp = '\0';
  3188.  
  3189.                 free(*headents[i].realaddr);
  3190.                 *headents[i].realaddr = bufp;
  3191.             }
  3192.             else{
  3193.                 emlwrite("Can't make room to pack header field.", NULL);
  3194.                 retval = FALSE;
  3195.             }
  3196.         }
  3197.  
  3198.         if(retval != FALSE){
  3199.         while(line != NULL){
  3200.                 strcat(*headents[i].realaddr, line->text);
  3201.         if(line->text[0] && line->text[strlen(line->text)-1] == ',')
  3202.           strcat(*headents[i].realaddr, " ");
  3203.  
  3204.                 line = line->next;
  3205.             }
  3206.         }
  3207.  
  3208.         i++;
  3209.     }
  3210.     return(retval);    
  3211. }
  3212.  
  3213.  
  3214.  
  3215. /*
  3216.  * zotheader - free all malloc'd lines associated with the header structs
  3217.  */
  3218. zotheader()
  3219. {
  3220.     register struct headerentry *i;
  3221.   
  3222.     for(i = headents; headents && i->name; i++)
  3223.       zotentry(i->hd_text);
  3224. }
  3225.  
  3226.  
  3227. /*
  3228.  * zotentry - free malloc'd space associated with the given linked list
  3229.  */
  3230. zotentry(l)
  3231. register struct hdr_line *l;
  3232. {
  3233.     register struct hdr_line *ld, *lf = l;
  3234.  
  3235.     while((ld = lf) != NULL){
  3236.     lf = ld->next;
  3237.     ld->next = ld->prev = NULL;
  3238.     free((char *) ld);
  3239.     }
  3240. }
  3241.  
  3242.  
  3243.  
  3244. /*
  3245.  * zotcomma - blast any trailing commas and white space from the end 
  3246.  *          of the given line
  3247.  */
  3248. void
  3249. zotcomma(s)
  3250. char *s;
  3251. {
  3252.     register char *p;
  3253.  
  3254.     p = &s[strlen(s)];
  3255.     while(--p >= s){
  3256.     if(*p != ' '){
  3257.         if(*p == ',')
  3258.           *p = '\0';
  3259.         return;
  3260.     }
  3261.     }
  3262. }
  3263.  
  3264.  
  3265. #ifdef    MOUSE
  3266. #undef    HeaderEditor
  3267.  
  3268. /*
  3269.  * Wraper function for the real header editor. 
  3270.  * Does the important tasks of:
  3271.  *    1) verifying that we _can_ edit the headers.
  3272.  *    2) acting on the result code from the header editor.
  3273.  */
  3274. int
  3275. HeaderEditor(f, n)
  3276.      int f, n;
  3277. {
  3278.     int  retval;
  3279.     
  3280.     
  3281. #ifdef _WINDOWS
  3282.     /* Sometimes we get here from a scroll callback, which
  3283.      * is no good at all because mswin is not ready to process input and
  3284.      * this _headeredit() will never do anything.
  3285.      * Putting this test here was the most general solution I could think
  3286.      * of. */
  3287.     if (!mswin_caninput()) 
  3288.     return (-1);
  3289. #endif
  3290.  
  3291.     retval = HeaderEditorWork(f, n);
  3292.     switch (retval) {
  3293.     case -2:
  3294.     retval = forwpage (0,1);
  3295.     break;
  3296.     case -3:
  3297.     retval = mousepress(0,0);
  3298.     break;
  3299.     }
  3300.     return (retval);
  3301. }
  3302. #endif
  3303.